Netlogo variables as txt (csv) files - variables

I am very new to Netlogo.
Linking a GIS file, I want to make a model simulating people's spending on food in reaction to the air pollution such as PM (particular matter). Of course, I also need to consider other variables such as air temperature, precipitation, and so forth. I have a panel data of food-consumption spending, air pollution and all other variables, collected by neighborhood.
My question is whether we can make a variable that changes by time, meaning when tick advances, the value of the variable changes. For example, agent may react to the air pollution in their neighborhood, and alter their food consumption behavior. I want to make a variable of "air pollution" for the neighborhood (patch) and that would change by time (following the data I collected). So somehow, I would like to link a text file of each variable and let net logo read it through when tick advances.
Is there any way to do that? Your advice would be very much appreciated.

I don't know how to link to a file and just pull one item. That sounds like accessing a database and I don't see or know of ( yet, just me ) a direct interface from netlogo to a database system.
Someone could write such a thing using Python, say, and use the interface to Python to pull the data you need. Or you could use the interface to R again with a little tweaking as certainly R has some very nice ways to read in CSV files and make them accessible on an item by item basis by the right clever statement in R.
Both of those would be fun to write. Anyone?
Lacking that, you COULD just read ALL of your data into NetLogo and populate , say, a list (indexed conceptually by tick) of lists ( of the 4 or 5 variables that change by tick ), then use a Netlogo statement to pull the right list indexed by tick ( plus or minus one, watch the zero versus one indexing. ) when you need it, and then parse that list to pull the variables out of it. That would certainly work.
I might write and post such a list of list solution this afternoon. Depends on the weather. Meanwhile, here's a related example I wrote once of reading in a CSV file and writing the variables to some patches-own variables, which is most of what you need.
I think this code works ( with a file to read of course. )
extensions [csv ]
globals [ XC YC ]
patches-own [
country
population
emissions_mass
vulnerability
jetfuel_civilian
climate_finance
]
to setup
clear-all
file-close
;; double the slashes so this works on a Windows computer
;; set-current-directory "C:\\Users\\wades\\Documents\\netlogo\\countries" ;
;; but simpler once just the let the user navigate to the file
;; file-open "countries.csv"
let mypath "???"
set-current-directory user-directory
file-open user-file
let headers file-read-line
print (word "There are the columns: " headers);
let headlist csv:from-row headers
let colcount length headlist
print (word "this many columns: " colcount)
let i 0
repeat colcount [
let determ item i headlist
print (word "column " i " is " determ)
set i ( i + 1 )
]
print "================================"
let nextline file-read-line
print ("skipping the second row of header info")
print nextline
print " "
while [ not file-at-end? ]
[
set nextline file-read-line
let mydata csv:from-row nextline
print "=============================================="
print (word "next incoming line to process is: " nextline )
print mydata;
set XC item 1 mydata
set YC item 2 mydata
print (word " data for patch (" XC ", " YC " )" )
let mytarget one-of patches with [ pxcor = XC and pycor = YC ]
ask mytarget
[
set country item 0 mydata
set population item 3 mydata
set emissions_mass item 4 mydata
set vulnerability item 5 mydata
set jetfuel_civilian item 6 mydata
set climate_finance item 7 mydata
set pcolor blue
set plabel country
]
print " that patch should now be blue"
]
file-close;
reset-ticks
end
to go
stop
tick
end

Here's sample code that reads time-series data from a csv file and puts it into a list-of-lists to pull later.
extensions [csv ]
globals [ list-of-lists ]
to setup
clear-all
reset-ticks
;; let max-rows-to-read 3 ;; for testing purposes
file-close
;; double the slashes so this works on a Windows computer
;; set-current-directory "C:\\Users\\wades\\Documents\\netlogo\\countries" ;
;; but simpler once just the let the user navigate to the file
;; file-open "countries.csv"
let mypath "???"
;; set-current-directory user-directory
file-open user-file
let headers file-read-line
print (word "There are the columns: " headers);
let headlist csv:from-row headers
let colcount length headlist
print (word "Found this many columns: " colcount)
let i 0
repeat colcount [
let determ item i headlist
print (word "column " i " is " determ)
set i ( i + 1 )
]
print "================================"
;; skip lines if you need to
; let nextline file-read-line
; print ("skipping the second row of header info")
; print nextline
; print " "
let rowcount 0
set list-of-lists [ ]
while [ not file-at-end? ]
[
set rowcount rowcount + 1
; if rowcount > max-rows-to-read [ print "list of lists: " show list-of-lists file-close stop]
let nextline file-read-line
let mydata csv:from-row nextline
print "=============================================="
print (word "next incoming line to process is: " nextline )
print mydata;
let onelist ( list
item 0 mydata
item 1 mydata
item 2 mydata )
set list-of-lists lput onelist list-of-lists
]
show list-of-lists
file-close;
end
to go
let indexer ticks ;; maybe you want this offset from ticks by some amount.
ifelse ( indexer < length list-of-lists )
[
let datalist item indexer list-of-lists
let time item 0 datalist
let temp item 1 datalist
let smog item 2 datalist
print (word "At tick " indexer " processing this list: " datalist )
print "yielding: "
print (word "At tick " indexer " time = " time " temp= " temp " and smog=" smog "\n")
]
[
print "All done. Have a nice day!"
]
tick
end

Related

Netlogo: Can one access a turtle variable with a concatenated word?

I have edited the code to focus on the issue:
globals [num_periods]
breed [observeds observe]
observeds-own [potential0 potential1 potential2 potential3 potential4 potential5 potential6 potential7 potential8 potential9
]
to setup
clear-all
reset-ticks
ask patches [set pcolor white]
create-observeds 1 [ setxy random-xcor random-ycor set shape "square" set color green
set potential0 random 0 set potential1 random 0 set potential2 random 0 set potential3 random 0 set potential4 random 0
set potential5 random 0 set potential6 random 0 set potential7 random 0 set potential8 random 0 set potential9 random 0
]
end
to go
ask observeds [
let test1 (word "potential" who) ;; this should output potential0 a variable
let test2 runreport [test1]
print test1 ;;prints potential0
ask observeds with [test2 = who][
move ]] ;; trying to move the turtle
end
to move
right random 360
fd 1
end
As there is only one agent it's who = 0. I have set all of the variables potentialx, x = [0,9], to zero. I have then 'created' the word 'potential0' by concatenating the work potential + who of the agent. Then I have asked all agents with potential0 to move and nothing happens.
Thanks,
Kevin
You will want to use runresult for this.
runresult takes whatever string it is applied to and treats it as a reporter rather than a string. If that string contains a number it returns a number. If it contains a variable name, it treats it as a variable.
So the following should solve your problem
ask observeds with [runresult test1 = who][
move
]
Use let test1 runresult (word ("potential" who) to form the string potential0. Subsequent commands will then recognise this as a reporter

How can I read values from a configuration file (text)?

I need to read values (text) from a configuration file named .env and assign them to variables so I can use them later in my program.
The .env file contains name/value pairs and looks something like this:
ENVIRONMENT_VARIABLE_ONE = AC9157847d72b1aa5370fdef36786863d9
ENVIRONMENT_VARIABLE_TWO = 73cad721b8cad6718d469acc42ffdb1f
ENVIRONMENT_VARIABLE_THREE = +13335557777
What I have tried so far
read-values.red
Red [
]
contents: read/lines %.env
env-one: first contents
env-two: second contents
env-three: third contents
print env-one ; ENVIRONMENT_VARIABLE_ONE = AC9157847d72b1aa5370fdef36786863d9
print env-two ; ENVIRONMENT_VARIABLE_ONE = 73cad721b8cad6718d469acc42ffdb1f
print env-three ; ENVIRONMENT_VARIABLE_ONE = +13335557777
What I'm looking for
print env-one ; AC9157847d72b1aa5370fdef36786863d9
print env-two ; 73cad721b8cad6718d469acc42ffdb1f
print env-three ; +13335557777
How do I continue or change my code and parse these strings such as the env- variables will contain just the values?
env-one: skip find first contents " = " 3
See help for find and skip
Another solution using parse could be:
foreach [word value] parse read %.env [collect some [keep to "=" skip keep to newline skip]] [set load word trim value]
This one will add the words to the global context ENVIRONMENT_VARIABLE_ONE will be AC9157847d72b1aa5370fdef36786863d9 and so on.

Powershell Excel - Using new range for search gives errors for old range

I am a complete beginner to Microsoft PowerShell, but I have worked with C++, Java, and C#. I was working on this small script for my job, and I came across a strange issue that is likely due to me not properly understanding how ranges work. This script is supposed to open up an Excel workbook, search each sheet for a name, and give the names that match along with their information. The problem is when I set the range again to search for the starting column index of information (in this case, the column to the right of the column labeled "Description"), it breaks the range when it searches for more than one match of the same last name.
I have a do-while loop that uses worksheet.range.entirerow.findnext() so that I can find multiple with the same last name. This works until I used a new range, worksheet.range.entirecolumn.find(). This is my latest code of what I tried, but I have already tried hardcoding $Range to 5 (which worked, but I want it to be dynamic) or used a new variable $RowRange (which didn't fix the issue). If I understood correctly, the range is like the current selection of two or more cells, so why can I not reset it or use a new variable? It does not loop, so I only keep finding the first name in each sheet.
P.S. As a side question, I had an issue of shutting down the process of this excel document I want to open in the background without shutting down other Excel workbooks. For some reason, using Get-Process EXCEL | Stop-Process -Force; shuts down ALL of my open workbooks. I commented it out, but I'm worried about the process not quite ending when it's done executing this code.
# Prepare output file for results
$FileName = "TEST";
$OutputFile = "Results.txt";
Remove-Item $OutputFile;
New-Item $OutputFile -ItemType file;
$Writer = [System.IO.StreamWriter] $OutputFile;
Clear-Host
Write-Host Starting...
# Start up Excel
$Excel = New-Object -ComObject Excel.Application;
$File = $FileName + ".xlsx";
# Prompt user for last name of person to search for (and write to the Results.txt output file)
Clear-Host
Write-Host Search for users in each region by their last name.
$SearchLastName = Read-Host -Prompt "Please input the person's last name";
Write-Host Searching for person...;
$Writer.WriteLine("Name Search: " + $SearchLastName);
$Writer.WriteLine("");
# Then open it without it being made visible to the user
$Excel.Visible = $false;
$Excel.DisplayAlerts = $true;
$Excel.Workbooks.Open($File);
# For each worksheet, or tab, search for the name in the first column (last names)
$Excel.Worksheets | ForEach-Object{
$_.activate();
$Range = $_.Range("A1").EntireColumn;
# Note: To search for text in the ENTIRETY of a cell, need to use the find method's lookat
# parameter (use 1). Otherwise, if searching for Smith, Nesmith also gets detected.
$SearchLast = $Range.find($SearchLastName,[Type]::Missing,[Type]::Missing,1);
$Writer.WriteLine($_.name + ": ");
if ($SearchLast -ne $null) {
$FirstRow = $SearchLast.Row;
do {
# If a first name was found, get the first name too
$FirstName = $_.Cells.Item($SearchLast.Row, $SearchLast.Column + 1).value();
# Then display in proper order
$Writer.WriteLine(" " + $SearchLast.value() + "," + $FirstName);
# From here, find the relevant information on that person
# Search for the column labeled "Description", the starting column is the next one, ending column is the number of used columns
$BeginCol = $_.Range("A1").EntireRow.find("Description",[Type]::Missing,[Type]::Missing,1).Column + 1;
$MaxColumns = $_.UsedRange.Columns.Count;
# Check each column for relevant information. If there are no extra rows after "Description" just skip
for ($i = $BeginCol; $i -le $MaxColumns; $i++) {
# The information of the current cell, found by the row of the name and the current row
$CurrentCell = $_.Cells.Item($SearchLast.Row, $i);
# Only add the description if it exists.
if (!([string]::IsNullOrEmpty($CurrentCell.value2))) {
$Description = $_.Cells.Item(1,$i).text();
# Concatenate the description with it's information.
$Display = " - (" + $Description + ": " + $CurrentCell.text() + ")";
# Display the information
$Writer.WriteLine($Display);
}
}
$Writer.WriteLine("");
# Keep searching that name in the current workbook until it finds no more
$SearchLast = $Range.FindNext($SearchLast);
} while (($SearchLast -ne $null) -and ($SearchLast.Row -ne $FirstRow));
} else {
$Writer.WriteLine("Not Found");
}
$Writer.WriteLine("");
};
# Cleaning up the environment
$Writer.close();
$Excel.Workbooks.Item($FileName).close();
$Excel.Quit();
# Force quit the Excel process after quitting
# Get-Process EXCEL | Stop-Process -Force;
# Then remove the $Excel com object to ready it for garbage collection
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel);
# Then, open up the Results.txt file
Invoke-Item Results.txt;

REBOL layout: How to create layout words automatically - word has no context?

Using the REBOL/View 2.7.8 Core, I would like to prepare a view layout beforehand by automatically assigning words to various layout items, as in the following example.
Instead of
prepared-view: [across
cb1: check
label "Checkbox 1"
cb2: check
label "Checkbox 2"
cb3: check
label "Checkbox 3"
cb4: check
label "Checkbox 4"
]
view layout prepared-view
I would thus like the words cb1 thru cb5 to be created automatically, e.g.:
prepared-view2: [ across ]
for i 1 4 1 [
cbi: join "cb" i
cbi: join cbi ":"
cbi: join cbi " check"
append prepared-view2 to-block cbi
append prepared-view2 [
label ]
append prepared-view2 to-string join "Checkbox " i
]
view layout prepared-view2
However, while difference prepared-view prepared-view2 shows no differences in the block being parsed (== []), the second script leads to an error:
** Script Error: cb1 word has no context
** Where: forever
** Near: new/var: bind to-word :var :var
I've spent hours trying to understand why, and I think somehow the new words need to be bound to the specific context, but I have not yet found any solution to the problem.
What do I need to do?
bind prepared-view2 'view
view layout prepared-view2
creates the correct bindings.
And here's another way to dynamically create layouts
>> l: [ across ]
== [across]
>> append l to-set-word 'check
== [across check:]
>> append l 'check
== [across check: check]
>> append l "test"
== [across check: check "test"]
>> view layout l
And then you can use loops to create different variables to add to your layout.
When you use TO-BLOCK to convert a string to a block, that's a low-level operation that doesn't go through the "ordinary" binding to "default" contexts. All words will be unbound:
>> x: 10
== 10
>> code: to-block "print [x]"
== [print [x]]
>> do code
** Script Error: print word has no context
** Where: halt-view
** Near: print [x]
So when you want to build code from raw strings at runtime whose lookups will work, one option is to use LOAD and it will do something default-ish, and that might work for some code (the loader is how the bindings were made for the code you're running that came from source):
>> x: 10
== 10
>> code: load "print [x]"
== [print [x]]
>> do code
10
Or you can name the contexts/objects explicitly (or by way of an exemplar word bound into that context) and use BIND.

Defining Variables Prior to use in TCL

I'm brand new to TCL, and am trying to wrap my brain around all the usages of "", {}, and [] that it uses. Something I'm used to doing in other languages is defining my variables prior to use, at the beginning of the application. The below code works:
puts "Please enter an integer of choice to be added: "
flush stdout
gets stdin intAnswer
puts "Please enter a second integer of choice to be added: "
flush stdout
gets stdin intAnswerTwo
puts "Please enter a third integer of choice to be added: "
flush stdout
gets stdin intAnswerThree
puts "The total of the three integers is: [expr $intAnswer + $intAnswerTwo + $intAnswerThree]"
What I'm wanting to do is define the variables prior to use. As such:
set intAnswer 0
set intAnswerTwo 0
set intAnswerThree 0
set intTotal 0
This code, placed at the beginning, doesn't work with the rest of the code. What am I missing?
The code looks absolutely fine to me, though [expr {$intAnswer + $intAnswerTwo + $intAnswerThree}] would be better (as it stops potential reinterpretation of the variables' contents, which would be both a safety and performance issue).
However, if you really want to have integers from the user, you need to validate their input. This is most easily done by writing a procedure to do the job so you can reuse it (i.e., you refactor the code to get a value so you can use a more sophisticated version and get it right once):
proc getIntFromUser {message} {
# Loop forever (until we [exit] or [return $response])
while true {
puts $message
flush stdout
set response [gets stdin]
# Important to check for EOF...
if {[eof stdin]} {
exit
}
# The validator (-strict is needed for ugly historical reasons)
if {[string is integer -strict $response]} {
return $response
}
# Not an integer, so moan about it
puts "\"$response\" is not an integer!"
}
}
Now you have that procedure, the rest of your code can become:
set intAnswer [getIntFromUser "Please enter an integer of choice to be added: "]
set intAnswerTwo [getIntFromUser "Please enter a second integer of choice to be added: "]
set intAnswerThree [getIntFromUser "Please enter a third integer of choice to be added: "]
puts "The total of the three integers is: [expr {$intAnswer + $intAnswerTwo + $intAnswerThree}]"
The art of writing good Tcl code (or good code in pretty much any other language) is knowing what are the good points to refactor. A good starting point is “if you do it twice or more, do it once and share”. It's doubly good if you can give the procedure a good name and clear interface, a clear indication that you've got it right. Indeed, you could also go for:
set total [expr {
[getIntFromUser "Please enter an integer of choice to be added: "] +
[getIntFromUser "Please enter a second integer of choice to be added: "] +
[getIntFromUser "Please enter a third integer of choice to be added: "]
}]
puts "The total of the three integers is: $total"
The results observed by the user will be identical.