I would like to sum up one numeric properties (AT1) of patches that share the same ID and store the value for that ID (procedure simulation here-below). I started with the idea of looping through the patches to find the ones that share the same ID (based on a external file).
See below a reproducible code that is not working, it is printing the sum for all patches one after another but not for just one ID, I tried several ways.
globals [
AT-data
ABC
area
]
patches-own [
ID
AT1
AT2
seed
sum_AT1
]
to setup
;; here I just create patches with different values that also appear in the list
ca
set ABC [ "A" "B" "C" "D" "E" "F" "G" "H" "I" ]
ask patches [ set seed random 10 set ID one-of ABC
ifelse (seed = 4)
[ set pcolor orange] [set pcolor white]
]
end
to load
reset-timer
; first, we load the database file
; We check to make sure the file exists first
ifelse ( file-exists? "AT_data.txt" )
[
; We are saving the data into a list, so it only needs to be loaded once.
set AT-data []
file-open "AT_data.txt"
while [ not file-at-end? ]
[
; file-read gives variables stored in a double list
; Each iteration we append the next three-tuple to the current list: ID AT1 AT2
set AT-data sentence AT-data (list (list file-read file-read file-read))
]
user-message "File loading complete!"
file-close
assign-data
stop
]
[ user-message "There is no AT_data.txt file in current directory!" ]
file-close-all
print timer
end
to assign-data
reset-timer
ask patches with [seed = 4] [
let i 1
while [i < length AT-data] [
let current-inner-list item i AT-data
ifelse (ID = item 0 current-inner-list)
[ set AT1 item 1 current-inner-list set AT2 item 2 current-inner-list
stop]
[ set i i + 1 ]
]
]
print timer
end
to simulation
reset-timer
ask patches [
let i 1
while [i < length AT-data] [
let current-inner-list item i AT-data
ifelse (ID = item 0 current-inner-list)
;; I tried with and without this following line
;[ask patches with [ID = item 0 current-inner-list] [
[ set area area + AT1
print area
print ID
stop
]
;]
;; this one is an alternative
;[ print sum [AT1] of patches with [ID = item 0 current-inner-list]
;print ID
;]
[ set i i + 1 ]
]
]
print timer
end
AT_data.txt is
"A" 65 81
"B" 21 71
"C" 54 18
"D" 23 41
"E" 85 27
"F" 35 88
"G" 29 4
"H" 78 2
"I" 99 60
Thanks for your time !
My first remark is that you have patches asking patches again in simulation.
Your second solution was the simpler one to work with. The main thing here was to take it out of patch context and let the observer run it.
For printing outputs, I suggest using a format such as print (word current-ID ": " current-sum). That is much cleaner for when you quickly want to check it after running the model.
to simulation-2
reset-timer
let i 0
while [i < length AT-data] [
let current-inner-list item i AT-data
let current-ID item 0 current-inner-list
let current-sum sum [AT1] of patches with [ID = current-ID]
print (word current-ID ": " current-sum)
set i i + 1
]
print timer
end
For your first solution, your problem was that you only had a single area variable that you incremented. In the following example, I made area into a list, with the same length as AT-data, containing lists. Each inner list consists of an ID and a counter set to 0 [["A" 0] ["B" 0] ... ["I" 0]].
For this, I use the map procedure. map takes each separate element of a list, does a certain operation with it, and return them all as a new list. It is in general a very useful procedure to learn when you will be working with lists.
Next I iterate through all patches as you did and increment the counter of my area list for the correct ID. I have two different versions of this incrementing. The first one has a lot of local variables to clearly show how it works. You dig out the correct sublist, then dig out the correct variable, increment that variable, replace it in the sublist and replace the sublist in the main list. The second one does exactly the same but in a single line of code.
to simulation-1
reset-timer
set area map [inner-list -> list item 0 inner-list 0] AT-data ;creates a new list of lists of the form [["A" 0] ["B" 0] ... ].
ask patches [
let i 0
while [i < length AT-data] [
let current-inner-list item i AT-data
ifelse (ID = item 0 current-inner-list)
[ let inner-area-list item i area ;grab the correct innerlist
let increased-count item 1 inner-area-list + AT1 ;increment the second part of this inner list
set inner-area-list replace-item 1 inner-area-list increased-count ;put the incremented count back into the inner list
set area replace-item i area inner-area-list ;put the inner list back into the complete list
;; all these can be combined into a single line of code but that is more prone to errors
;set area replace-item i area (replace-item 1 item i area (item 1 item i area + AT1))
stop
]
[ set i i + 1 ]
]
]
print area
print timer
end
It was only after making this entire nested list structure that I thought of the fact that you can do it with a normal list where you only have the different counters and not the ID's but this structure does make it very compact and clear and good to use for followup processing.
Related
Here is what I want to do:
I want a team consists of 3 turtles in a group.
All turtles need to store their own ID and teammatesID inside the variable called teammatesID. (Imagine you want to create a group, you will put your name and your friends' name in a list, that's why this variable need to store ownID, I am bad in explaning things..)
Instead of just showing (agentset,3 turtles), I need them to be able to show all teammates ID.
After they have gathered all 3 members, they will get teamID.
The problem here is I don't know how to make these turtles store their own ID and teammatesID in variable called teammatesID. In a group, they supposed to have 3 different members. These teammates are not supposed to be from the same group. And how to give them teamID after they get all members.
Here is my coding:
global
[ teamID]
turtles-own
[
myID
teammatesID
]
to setup
clear-all
set-default-shape turtles "arrow"
create-turtles 10
ask turtles [ set myID who]
reset-ticks
setup-group
end
to setup-group
set teamID []
let agent one-of other turtles
let nearestNeighbor one-of other turtles in-radius 1
set teamID = 0
ask agent with [ teammatesID < 3]
[ ask nearestNeighbor with [teammatesID < 3]
[ ;set teammatesID = myID ; here is the problem, I need to make sure they did not save the same turtle in one group.
show teammatesID]]
ask agent with [teammatesID > 3]
set teamID fput teamID teamID
end
I appreciate your extra time here. Thank you.
If teams are random, I don't think you need a loop for this as ask will call the turtles in a random order anyway.
I think you should still make use of agentsets here as it makes certain things easier. For example, once turtles know their (agentset,3 turtles), you can easily query that agentset for the myIDs or whatever variable you're after.
I'm not entirely clear on the purpose of teamID so I may be off base here, but I don't think you want it as a global variable if the teamIDs are to be unique to each group of three turtles. You'll probably want it to be a turtles-own variable as well.
Here is an example that incorporates the above ideas. With this setup:
turtles-own [ myteamset teamID myID teammatesID ]
to setup
ca
crt 10 [
set myID who
set myteamset nobody
set teammatesID [ ]
]
setup-groups
print remove-duplicates [teammatesID] of turtles
print sort [teamID] of turtles
reset-ticks
end
And the setup-groups procedure (more detail in comments):
to setup-groups
ask turtles [
; If you don't have a team yet
if myteamset = nobody [
; Make a temporary agent-set out of all other turtles that
; are also not yet part of a team
let possible-teammates other turtles with [ myteamset = nobody ]
; If there are at least two more turtles, make a new team:
ifelse count possible-teammates > 1 [
; Make a team out of myself and two possible teammates
set myteamset ( turtle-set self n-of 2 possible-teammates )
; Make a temporary variable to pass on to my entire team
; (yourself included) for the team ids and the team members.
; Also, assign a random teamID to the whole team
let ids sort [myID] of myteamset
let teammmembers myteamset
let tempteam random 1000
ask myteamset [
set teammatesID ids
set myteamset teammmembers
set teamID tempteam
]
] [
; If there aren't enough turtles to form a new team,
; print a warning to the console.
show "Not enough turtles to make a new team!"
]
]
]
end
Let me know if that's kind of what you're after, hope it helps.
Edit- As per your comments:
To get sequential team numbers, you can make use of a simple counter that gets assigned to a team and then incremented for the next one- modified setup-groups would look like:
to setup-groups
; Create a temporary variable to use as a counter
let teamCounter 1
ask turtles [
if myteamset = nobody [
let possible-teammates other turtles with [ myteamset = nobody ]
ifelse count possible-teammates > 1 [
set myteamset ( turtle-set self n-of 2 possible-teammates )
let ids sort [myID] of myteamset
let teammmembers myteamset
; Assign the current teamCounter as team number, then
; increment it by one for the next team
let tempteam teamCounter
set teamCounter teamCounter + 1
ask myteamset [
set teammatesID ids
set myteamset teammmembers
set teamID tempteam
]
] [
show "Not enough turtles to make a new team!"
]
]
]
end
The second question may be worth a new question on its own, depending on how in depth you're hoping to get, but here is one very simplistic approach where you just get all unique team IDs and have one turtle with that ID have their team move together:
to move-in-groups
; Get the unique team IDs
let teamNumbers remove-duplicates [teamID] of turtles
; Get one member of each team to ask all members
; of its team (itself included) to move
foreach teamNumbers [
tn ->
ask one-of turtles with [ teamID = tn ] [
let newHeading heading + random 60 - 30
if myteamset != nobody [
ask myteamset [
set heading newHeading
fd 1
]
]
]
]
end
Edit 2: NetLogo 5-friendly version
to move-in-groups
; Get the unique team IDs
let teamNumbers remove-duplicates [teamID] of turtles
; Get one member of each team to ask all members
; of its team (itself included) to move
foreach teamNumbers [
ask one-of turtles with [ teamID = ? ] [
let newHeading heading + random 60 - 30
if myteamset != nobody [
ask myteamset [
set heading newHeading
fd 1
]
]
]
]
end
I agree with Luke that the most appropriate way to deal with teams is as agentsets, not the complicated allocating of identifiers. This is what the code looks like to do that. Note that I haven't addressed the movement part of your question.
globals [max-teamsize]
turtles-own
[ teamID
teammates
]
to testme
clear-all
create-turtles 100
[ setxy random-xcor random-ycor
set teamID 0
set teammates nobody
]
set max-teamsize 3
setup-groups
reset-ticks
end
to setup-groups
; assign turtles to team by allocating a team number
let counter 1
ask turtles
[ if teamID = 0
[ set teamID counter
let potential-teammates turtles with [teamID = 0]
ask n-of min (list (max-teamsize - 1) (count potential-teammates)) potential-teammates
[ set teamID counter
]
set counter counter + 1
]
]
; store teammates as agentset of other turtles in team
ask turtles
[ set teammates other turtles with [teamID = [teamID] of myself]
]
end
I am a beginner. I already checked the programming guidance dictionary.
I am considering a two-lane road (eg, road 1, road 2) model.
And also I am considering a model in which the turtle specified by the specified patch((10 0) and (20 2)) stops for 10 ticks.
However, I do not know how to write and specify the specific parameter for xcor and ycor for each road (Eg xcor and ycor on road 1, xcor and ycor on road 2).
And also I do not know how to write and controol the parameter "speed" within the set-speed syntax.
The following is the sample small model. To avoid complication, this sample model has only one road. This sample model is failing and the turtle does not stop at patch(10 0).
Probably I need your advice. Thank you.
globals [ count-tick ]
turtles-own [ speed flag-A ]
to setup
clear-all
resize-world 0 50 min-pycor max-pycor
ask patches [ setup-road ]
reset-ticks
end
to setup-road
if ( pycor < 1 ) and ( pycor > -1 ) [ set pcolor white ]
end
to create-car
crt 1 [
set color blue
setxy min-pxcor 0
set heading 90
set speed 1
]
end
This is the main body of the model.
to go
if (count turtles-on patch 0 0 = 0) [
create-car
ask (turtles-on patch 0 0) [
set flag-A FALSE
]
]
ask (turtles-on patch 10 0) [
set flag-A TRUE
set count-tick 10
]
if count-tick > 0 [
set count-tick count-tick - 1
ask (turtles-on patch 10 0) with [flag-A = TRUE]
[
set color red
set speed 0
]
]
if count-tick = 0 [
ask (turtles-on patch 10 0) with [flag-A = TRUE]
[
set speed 1
set flag-A FALSE
]
]
if (count turtles-on patch max-pxcor 0 > 0) [
ask min-one-of turtles [who][
die
]
]
set-speed
tick
end
This is the parallel update to control the speed.
to set-speed
ask turtles with [ xcor < 10 ] [
let turtle-ahead one-of turtles-on patch-ahead 1
ifelse turtle-ahead = nobody
[ set speed 1
fd speed
]
[ set speed 0
]
]
ask turtles with [ 10 < max-pxcor ] [
let turtle-ahead one-of turtles-on patch-ahead 1
ifelse turtle-ahead = nobody
[ set speed 1
fd speed
]
[ set speed 0
]
]
end
Okay, as a general rule, add ONE element at a time to your model. Test that element and then only add the next element once everything works. In your case you are trying to do several things without any of them working - moving cars, pausing them for 10 ticks, making one of them die at the end of the road, doing something unspecified with their speed, and probably other things I didn't immediately notice.
You also have several conceptual problems here - the biggest is that count-tick is a turtle variable, but you are treating it as a global variable because if count-tick... should be inside an ask turtles block. Think about it this way, if you have 10 cars created, there are 10 copies of the variable count-tick so which one are you checking with the if statement.
You also haven't told your turtles to move, but that may be in the code you haven't shown. Keeping as much of your code as I can, this is what I think you are trying to do. This will create a car at the left, have it move to the right, pause at the correct place for 10 ticks and turn red, then move again, killing it when it gets to the end.
globals [ count-tick ]
turtles-own [ speed flag-A ]
to setup
clear-all
resize-world 0 50 min-pycor max-pycor
ask patches [ setup-road ]
reset-ticks
end
to setup-road
if ( pycor < 1 ) and ( pycor > -1 ) [ set pcolor white ]
end
to create-car
crt 1 [
set color blue
setxy min-pxcor 0
set heading 90
set speed 1
set flag-A FALSE
]
end
to go
if (count turtles-on patch 0 0 = 0) [
create-car
]
ask (turtles-on patch 10 0) [
set flag-A TRUE
set count-tick 10
]
ask (turtles-on patch 10 0) with [flag-A = TRUE] [
set color red
set speed 0
set count-tick count-tick - 1
if count-tick = 0 [
set speed 1
set flag-A FALSE
]
]
if (count turtles-on patch max-pxcor 0 > 0) [
ask min-one-of turtles-on patch max-pxcor 0 [who][
die
]
]
ask turtles [ forward speed ]
tick
end
i am basically trying to switch around an array of arrays; my initial data are:
array = [
[0,0,0],
[1,1,1]
]
the output should be:
[
[0,1],
[0,1],
[0,1]
]
however what i get is:
[]
i have tried doing the same thing without the loops but when i introduce them it just wont append!
see code here:
array = [
[0,0,0],
[1,1,1]
]
transformedArray = []
#add rows to transformed
for j in range(0, len(array) - 1):
transformedArray.append([])
#for each row
for i in range(0, len(array[0]) - 1):
#for each column
for k in range(0, len(array) - 1):
transformedArray[i].append(array[k][i])
can you help? i have not found any similar issues online so i am guessing i've missed something stupid!
Try nesting your loops:
array = [
[0,0,0],
[1,1,1]
]
transformedArray = [[0,0],[0,0],[0,0]]
# iterate through rows
for i in range(len(array)):
# iterate through columns
for j in range(len(array[0])):
transformedArray[j][i] = array[i][j]
for res in transformedArray:
print(res)
returns:
[0, 1]
[0, 1]
[0, 1]
Edited to Add explanation:
First, lists are defined as in this code above: aList = [ ... ] where an array would be defined as anArray = numpy.array([...]), so to the point of the comments above, this is list processing in the question, not true python array process. Next, elements are being added to the list by index, so there has to be a place to put them. I handled that by creating a list with 3 elements already in place. The original post would only create the first 2 rows and then have an index failure when the 3rd row is to be created. The nested for loops then iterate through the embedded lists.
You could do it by mapping a sequence of index-access operations over all the arrays:
for i in range( len( array[0] ) ):
transformedArray.append( map( lambda x: x[i], array ) )
I have a list of items, I want to be able to go through the list. start at index [0] if the item is in the board, add 1 to the index and keep going, if its not assign that value of x to computermove and break outside the loop . with the way I have it, the computermove is "b2" even though its in the list, shouldn't it add 1 to the index and loop back around, I don't understand that
list=["b2", "a1", "c3", "a3", "c1", "a2", "b3", "b1", "c2"]
board = {0:'b2', 1:' ', 2:' ',
3:' ', 4:'a1', 5:' ',
6:' ', 7:' ', 8:'c3',
}
for x in list:
if x in board:
x = x+1
else:
computermove= x
break
You seem to not understand how a for loop works.
You should not be increasing indexes manually(and x is not an index anyway), but let the for loop do that for you.
Your other problem is that x in board checks if x is in the keys of board, not its values.
Here's one possible way to make (that part of) your code work:
for x in list:
if x not in board.values():
computermove = x
break
That's really all the help I can give you with the amount of code/information you've shown.
Suppose if I want to have 10 element array each element is a list/map. I am doing this:
x = array(list(), 10)
x[1][[ "a" ]] = 1
Warning message:
In x[1][["a"]] = 1 :
number of items to replace is not a multiple of replacement length
>
Is this the right approach? I want each element of the array to be a map.
What you're calling an "array" is usually just called a list in R. You're getting tripped up by the difference between [ and [[ for lists. See the section "Recursive (list-like) objects" in help("[").
x[[1]][["a"]] <- 1
UPDATE:
Note that the solution above creates a list of named vectors. In other words, something like
x[[1]][["a"]] <- 1
x[[1]][["b"]] <- 1:2
won't work because you can't assign multiple values to one element of a vector. If you want to be able to assign a vector to a name, you can use a list of lists.
x[[1]] <- as.list(x[[1]])
x[[1]][["b"]] <- 1:2
If you really want to do this, then, because the elements of the lists in each element of the array do not have names, you can't index by a character vector. In your example, there is no x[1][[ "a" ]]:
> x[1][[ "a" ]]
NULL
If there are no names then you need to index by a numeric:
> x[1][[ 1 ]] <- 1
[1] 1
It would seem more logical to have a list though than an array:
> y <- vector(mode = "list", length = 10)
> y
[[1]]
NULL
[[2]]
NULL
[[3]]
NULL
[[4]]
NULL
[[5]]
NULL
....