Using to-reports while creating agents from a csv

2019-02-27 17:24发布

问题:

My question is a bit long. I really appreciate it if you could please read it all and I'd be very grateful for any piece of advice.

I have data related to 2 consumers as turtles who have rated laptops' features. The laptops have 2 kinds of features : size of the screen and battery life. Each has some levels. For example battery life has 5 hours, 12 hours, 24 hours, 30 hours. Data is stored in a csv file.

   12 13.5 14 15 5 12 24 30
1  1   2    1  3 2  2  4  5
2  4   3    1  2  1 1  2  3

I want to sum the rates of 2 levels of feature. For example for consumer 1 and 2, what is:

sum rate of screen size of 13.5 + rate of battery life 24

Since 13.5 and 24 may change later and I want to know for example the sum of rates of size of 14 and battery life of 5, I defined two functions using "to-reports" . Also since for example the value "12" is in the header row twice and represents both a size and a battery life, I made 2 subsets of the csv file one for screen size , the other for battery.

   12 13.5 14 15 
1  1   2    1  3 
2  4   3    1  2  

   5 12 24 30
1  2  2  4  5
2  1 1  2  3

First in the main program, the csv file is read and a turtle is assigned to each row, expecting to have two consumers.

to setup
  ca
  reset-ticks
  file-close-all
  file-open "turtle_details.csv"
 let headerrow csv:from-row file-read-line
 set Sc 13.5   ; at the beginning I want the rate of this screen size
 set Bat 24
   while [ not file-at-end? ] [
    let data csv:from-row file-read-line
      create-consumers 1 [
         set shape "person"
         set k k + 1
       print  obtain-Sc (Sc) + obtain-Bat (Bat)
      ]
   ]
  file-close-all
end

I assumed, first, row one is read and a consumer is generated. Now it goes to to-report to find obtain-Screen(13.5) which is 2, but I thought next time that obrain_Screen is called, again the csv opens and the cursor would still be at the beginning, but I want it to read the second row. And to extend it, I might need it to go further and further. To solve this, I defined a counter k that for example the first time this condition is checked : idx = 0 < k =1 so the first row is read. Then idx = idx + 1 , so nothing else is done.

to-report obtain-Screen[Sc]
  file-close-all ; close all open files

  file-open  "turtle_detailsSc.csv" 
  let headings csv:from-row file-read-line
  ifelse  is-number? position Sc headings
    [
      while [idx < k ]
      [ set fdata csv:from-row file-read-line
        set idx idx + 1
      ]
      report item position Sc headings fdata
]
[report 0.000000009]

Something similar for Bat. But it does not work and has errors. Any ideas how to improve to-reports? Thanks

Edit

Considering the data set to be like this :

   size12  size13.5  size14  size15  Battery5  Battery12 Battery24 Battery30 
1  1        *2*         1      3        2         2           *4*       5
2  4        3           3      2        1          1           2        3

I can now access the data set and for each consumer find their evaluation of a laptop they have purchased. For example, consumer 1 has a laptop with size of 13.5 and battery life 24.

Consumer 1 evaluation of size 13.5 = 2
Consumer 1 evaluation of battery 24 = 4
Overall evaluation of laptop = 2 + 4 = 6

I have defined a procedure "Find Eval" that when I need to know the evaluation of different consumers, it enables me to access the dataset and find the values.

To explain the data in the table more, a consumer has a laptop so can evaluate it pretty well, but for other features like how he evaluates a laptop with the screen size of 15, he might have some exposure or just by hearing from others he has filled in the table.

I want to keep these 2 consumers and monitor their attitudes about laptop features during 20 years. In year 2, consumer 1 possibly updated his system, so now his battery life is 30. This time what I need to do is to access the dataset and calculate

Consumer 1 evaluation of size 13.5 = 2

plus

Consumer 1 evaluation of battery 30 = 5



  Overall evaluation of laptop = 2 + 5 = 7

This time, I need to go to find the value for battery 30. I think when I repeat my code for 20 years, create-consumer will be repeated each time that I want to work with the dataset, so instead of keeping 2 consumers, each year, some new consumers will be created and the previous ones will be totally replaced.

The question is how I can create consumers once, but can access any data point in the data set for many times?

Thanks a lot,

回答1:

In response to your comment- got it, thought that might be the case. I will present an alternative approach that may or may not suit you, but it's probably how I would tackle this. The main difference is that I would store the lists for screen and battery ratings in a turtle variable, so you can easily access them after the fact without having to track counters. Given these variable declarations:

extensions [ csv ]

globals [ screen-headings battery-headings]

turtles-own [ 
  turtle-screen-list 
  turtle-battery-list
  turtle-screen-eval
  turtle-bat-eval
  turtle-sum-eval
  turtle-row-number
]

I would split headings of the csv file to the screen-headings and battery-headings globals.

Then, I would iterate through the next lines of the csv, as you did below, and split the lines in a similar way but into the turtles-own variables turtle-screen-list and turtle-battery-list. That way, each turtle is aware of its own ratings for each screen and battery so you can modify your evaluated screen or battery as needed.

Set the screen and battery of interest with screen-to-evaluate and battery-to-evaluate (or use a Chooser on the interface), then use a reporter (similar to what you had set up) that checks the position of the battery and screen of interest to return the current turtle's rating for each- this will set turtle-screen-eval and turtle-bat-eval. Finally, sum those last two values.

to setup
  ca
  reset-ticks
  file-close-all
  file-open "turtle_details.csv"
  let headings csv:from-row file-read-line
  set screen-headings sublist headings 0 4
  set battery-headings sublist headings 4 length headings

  let screen-to-evaluate 13.5
  let battery-to-evaluate 24

  while [ not file-at-end? ] [
    let data csv:from-row file-read-line
    create-turtles 1 [
      set turtle-screen-list sublist data 0 4
      set turtle-battery-list sublist data 4 length data
      set turtle-screen-eval turtle-screen-rating screen-to-evaluate
      set turtle-bat-eval turtle-battery-rating battery-to-evaluate
      set turtle-sum-eval turtle-screen-eval + turtle-bat-eval
    ]
  ]
  file-close-all

end

to-report turtle-screen-rating [sc]
  let pos position sc screen-headings
  let turt-screen-rate-value item pos turtle-screen-list
  report turt-screen-rate-value
end

to-report turtle-battery-rating [bc]
  let pos position bc battery-headings
  let turt-bat-rate-value item pos turtle-battery-list
  report turt-bat-rate-value
end

Of course, you can condense some of this as needed. I tested this setup with 4 rows (patterned after your example data) and it seemed to work ok. If you have a huge number of rows it might not work so well.

Edit:

If you end up replacing screen-to-evaluate and battery-to-evaluate with interface choosers (which I recommend- it allows you to quickly see the different values), you can update to the new values with something like:

to update-vals
  ask turtles [
    set turtle-screen-eval turtle-screen-rating scrn
    set turtle-bat-eval turtle-battery-rating batr
    set turtle-sum-eval turtle-screen-eval + turtle-bat-eval
  ]
end

where scrn and batr are the names of the choosers.

Or, if you want them to dynamically update, you can make an interface monitor that reports the following reporter :

to-report update
ask turtles [
    set turtle-screen-eval turtle-screen-rating scrn
    set turtle-bat-eval turtle-battery-rating batr
    set turtle-sum-eval turtle-screen-eval + turtle-bat-eval
  ]
  report "Dynamically updating."
end

With that one, as soon as you change the Chooser value, the turtles should immediately update their turtle-bat-eval, turtle-screen-eval, and turtle-sum-eval. Fun!

Edit 2

If your csv includes a column for row numbers like this:

row 12 13.5 14 15  5 12 24 30
1   1  2.0  1  3  2  2  4  5
2   4  3.0  1  2  1  1  2  3

I would recommend creating a turtle variable to store the row number used. I've now included one called turtle-row-number. Then, you would just have to include a line to update that variable from the first item in the list, which is the row number, and then modify your sublist values accordingly, to something like:

to setup-row-nums
  ca
  reset-ticks
  file-close-all
  file-open "turtle_details_2.csv"
  let headings csv:from-row file-read-line
  set screen-headings sublist headings 1 5
  set battery-headings sublist headings 5 length headings

  while [ not file-at-end? ] [
    let data csv:from-row file-read-line
    create-turtles 1 [
      set turtle-row-number first data 
      set turtle-screen-list sublist data 1 5
      set turtle-battery-list sublist data 5 length data
    ]
  ]
  update-vals
  file-close-all

end

Where update-vals is as shown above.