I want to alter a series of variables from a loop, but the following code is not working:
a: 10
b: 20
c: 30
print reduce [a b c] ; output is 10 20 30 as expected
varnames: [a b c] ; make a series of variables
foreach i varnames [ ; loop to convert each to 0
i: 0
]
print "After conversion loop: "
print reduce [a b c] ; values are still 10 20 30 (expected 0 0 0)
After running this code, I'd expect the values of a, b, c to be changed, but they're not:
>> print reduce [a b c]
10 20 30
Where is the problem?
>> set varnames: [a b c] [10 20 30]
== [10 20 30]
>> foreach i varnames [set i 0]
== 0
>> reduce varnames
== [0 0 0]
You've given i a value from the loop, and then given it a different value of 0, but you actually haven't altered the word that i referred to. set allows you to do this.
one more, but shortest solution
>> set [a b c] 0
== 0
>> reduce [a b c]
== [0 0 0]
one more
set set 'varnames [a b c] [0]
>> print probe varnames
[a b c]
0 0 0
Related
I have built a long nested list having the following profile:
set my-list [A 1 2 3 4] [B 5 6 7 8] [C 9 10 11 12]
I'd like to apply the meancommand to the fourth item of each nested lists, so in the example to
4 8 12
but without building a list in loop that would look like [4 8 12] (to save computing time).
Is it possible ?
using let comp mean (item i item 4 (my-list)) or let comp mean (item 4 (my-list)) aren't obviously working.
The answer would be useful to other part of the model that I'm building.
Thanks for your time.
The map primitive is very well suited for these sorts of calculations with lists. It allows you to perform a reporter separately for each part of a list and then returns the results as a new list.
let test-list [1 2 3]
show map [x -> x + 1] test-list
;> [2 3 4]
In your case, you would use map to cycle through your list of lists, and use the item primitive to extract the necessary number from each sublist (map [x -> item 4 x ] my-list). This then returns them as a list of which you can take the mean.
to check-mean-2
let my-list [["a" 1 2 3 4] ["b" 5 6 7 8] ["c" 9 10 11 12]]
let my-mean mean map [x -> item 4 x ] my-list
print my-mean
end
EDIT: Although mine looks more efficient on first glance, Matteo's version actually runs quicker (at least on my machine)
globals [my-list]
to setup
set my-list [["a" 1 2 3 4] ["b" 5 6 7 8] ["c" 9 10 11 12]]
end
to check-mean
let timer-list []
repeat 10 [
reset-timer
repeat 1000000 [
let the-sum 0
let i 0
while [i < length my-list] [
set the-sum (the-sum + item 4 (item i my-list))
set i i + 1
]
let my-mean the-sum / i
]
let the-timer timer ; ~0.207
show the-timer
set timer-list lput the-timer timer-list
]
show word "mean: " (mean timer-list) ; 0.210
end
to check-mean-2
let timer-list []
repeat 10 [
reset-timer
repeat 1000000 [
let my-mean mean map [x -> item 4 x ] my-list
]
let the-timer timer
show the-timer
set timer-list lput the-timer timer-list
]
show word "mean: " (mean timer-list) ; 0.235
end
ANOTHER EDIT: Finally two more versions using reduce instead of map. Version 3 is the fastest of them all but you should take notice of the fact that my-list has a 0 added to it in this version. This might make is slightly less conveniet for other purposes. You can also add this 0 to it during the calculation as seen in version 4, but that drives up the time again.
to check-mean-3
set my-list [0 ["a" 1 2 3 4] ["b" 5 6 7 8] ["c" 9 10 11 12]]
let timer-list []
repeat 10 [
reset-timer
repeat 1000000 [
let my-sum reduce [ [x y] -> x + item 4 y] my-list
let my-mean my-sum / (length my-list - 1)
]
let the-timer timer
show the-timer
set timer-list lput the-timer timer-list
]
show word "mean: " (mean timer-list) ; 0.170
end
to check-mean-4
set my-list [["a" 1 2 3 4] ["b" 5 6 7 8] ["c" 9 10 11 12]]
let timer-list []
repeat 10 [
reset-timer
repeat 1000000 [
let my-new-list fput 0 my-list
let my-sum reduce [ [x y] -> x + item 4 y] my-new-list
let my-mean my-sum / (length my-list - 1)
]
let the-timer timer
show the-timer
set timer-list lput the-timer timer-list
]
show word "mean: " (mean timer-list) ; 0.226
end
First things first: did you mean to say that such nested list is built such as the one below?
set my-list [[A 1 2 3 4] [B 5 6 7 8] [C 9 10 11 12]]
Note the extra pair of square brackets, that make this actually a list of lists. Even if that was the case, NetLogo wouldn't let you use that syntax:
Either because A, B and C are lacking quotation marks if you intend them to be strings;
Or because, if A, B and C are variables, NetLogo expects literal values (we can get around this problem by using (list ...) instead of []).
In the first case, it would have to be:
set my-list [["a" 1 2 3 4] ["b" 5 6 7 8] ["c" 9 10 11 12]]
In the second case, it would have to be:
set my-list (list (list a 1 2 3 4) (list b 5 6 7 8) (list c 9 10 11 12))
All of the above just to make sure we are all on the same page (in general, please make sure that the code you post in your question is valid for the language you are asking about. As you can see, it would save a lot of time and space!).
Anyway I imagine that what you come up with is something of this type:
[["a" 1 2 3 4] ["b" 5 6 7 8] ["c" 9 10 11 12]]
I would use a while loop to iterate through the inner lists. You can create a local variable to keep track of the sum of the numbers you extract as you iterate through the inner lists, and then divide that sum by the number of times you extracted a number:
to check-mean
let my-list [["a" 1 2 3 4] ["b" 5 6 7 8] ["c" 9 10 11 12]]
let the-sum 0
let i 0
while [i < length my-list] [
set the-sum (the-sum + item 4 (item i my-list))
set i i + 1
]
print the-sum / i
end
From the answers above and adding a step in the procedure (mean and sd for groups of items having the same "region" in the list), here-below is my final code using map as the mean and sd are already calculated in a while-loop. Moreover, I assumed that calculating manually the standard deviation would create even more lists of list and complicate the code.
to create-successor-list
set successor-list map [inner-list -> (list inner-list 0 0 ) ] region-data
let i 0
while [i < length region-data] [
let current-item item i region-data
set temp-profitability-list (filter [current-inner-list -> (item 1 current-inner-list = current-item)] profitability-list )
set prof-mean mean map [x -> item 4 x ] temp-profitability-list
set prof-sd standard-deviation map [x -> item 4 x ] temp-profitability-list
set successor-list replace-item i successor-list (replace-item 1 item i successor-list (prof-mean))
set successor-list replace-item i successor-list (replace-item 2 item i successor-list (prof-sd))
set i i + 1
set temp-profitability-list [ ]
]
end
I want to get values from 2 series into one but it is not working:
I have 2 series:
a: [1 2 3]
b: [4 5 6 7]
I want to get all values in one list, so that I can access them as allv/1, allv/2... allv/7.
Following is not working since it makes a series of series and not series of values:
allv: [a b]
print allv ; => prints all 6 values, but following do not work:
print allv/1 ; => gives a (desired is 1)
print allv/2 ; => gives b (desired is 2)
print allv/3 ; => gives none (desired is 3)
I tried following function to combine values in one series:
mycombine: function [ll][
temp: []
foreach l ll [
repeat i length? l [
temp: append temp l/:i ] ]
temp]
mycombine [a b]
But above gives error:
*** Script Error: length? does not allow word! for its series argument
*** Where: length?
*** Stack: rncombine
The series has got converted into word and is not working.
How can I solve this?
Just append
a: [1 2 3]
b: [4 5 6 7]
c: [8 9 10]
d: [11 12 13 14]
>> allv: append a b
== [1 2 3 4 5 6 7]
>> a
== [1 2 3 4 5 6 7]
does what you want. But beware even a contains all values, as you have appendend the values of b to the block a. If you want a new block you have to use
allv: append copy a b
If you want to append more series you can do
>> foreach x [a b c d] [ append [] reduce x]
== [1 2 3 4 5 6 7 8 9 10 11 12 13 14]
Instead of reduce also get x is working.
Another way of combining
>> compose [(a) (b) (c)]
== [1 2 3 4 5 6 7 8 9 10 ]
I have 2 lists:
alist: [a b c d]
blist: [1 2 3 4]
(In reality they are long lists). How can I assign variables in alist to corresponding values in blist in one go? Hence a becomes 1, b becomes 2 and so on.
I tried:
foreach i alist j blist [i: j]
But it give following error:
*** Script Error: j has no value
*** Where: foreach
*** Stack:
I also tried:
i: 1
while [true] [
if i > (length? alist) [break]
alist/i: blist/i
i: i + 1
]
But it also does not work:
*** Script Error: cannot set none in path alist/i:
*** Where: set-path
*** Stack:
alist/i and blist/i return none (on checking with print command).
Similar question are there for other languages also, e.g.: Parallel array assignment in PHP and Parallel assignment in Java? . Thanks for your help.
easy way, set one list to the other
>> set alist blist
== [1 2 3 4]
>> a
== 1
>> b
== 2
>> c
== 3
>> d
== 4
>> alist
== [a b c d]
>> reduce alist
== [1 2 3 4]
>> get alist/1
== 1
and the cumbersome way
>> forall alist [alist/1: blist/(index? alist) ]
>> i: 2
== 2
>> get alist/:i
== 2
I have a list with 20 items and I want to count the number of occurrences of each item in list. I know code below
to-report frequency [i lst]
report length filter [? = i] list
end
but I do not want to write 20 lines like
let C1 frequency 1 (list1)
let C2 frequency 2 (list1)
.
.
.
let C20 frequency 20 (list1)
That's:
map [frequency ? list1] n-values 20 [? + 1]
Sample run:
observer> set list1 [1 4 4 7 10 10 10 14]
observer> show map [frequency ? list1] n-values 20 [? + 1]
observer: [1 0 0 2 0 0 1 0 0 3 0 0 0 1 0 0 0 0 0 0]
For NetLogo 6, OP's function would be this:
to-report frequency [an-item a-list]
report length (filter [ i -> i = an-item] a-list)
end
Making a frequency list is revised like this:
map [ i -> frequency i list1] (n-values 20 [i -> i])
I want to Extract only nonzero numbers from python list .
This is what I am doing . but it isnt working it seems .
d=[num if num for num in d]
where d is my original list and again I want output in same list
In [5]: d =[1,2,3,0,0,9]
In [6]: d = filter(None,d)
In [7]: d
Out[7]: [1, 2, 3, 9]
Some timings:
In [30]: %timeit filter(None,d)
1000000 loops, best of 3: 727 ns per loop
In [31]: %timeit filter(lambda x: x != 0, d)
100000 loops, best of 3: 3.89 µs per loop
In [32]: %timeit [x for x in d if x != 0]
100000 loops, best of 3: 2.33 µs per loop
In [33]: %timeit [num for num in d if num]
100000 loops, best of 3: 2.04 µs per loop
As you only have numbers in your list filter(None,d) will work fine. If you had any other falsey values like empty lists [] etc.. it would also remove them.
d = [num for num in d if num]
EDIT
According to your comment below, by "extracting non-zero" you mean: "the way to map 0 and nonzero numbers from a list:
d = [num if num else 0 for num in d]
another way:
d = map(lambda x: 1 if x else 0, d)
You are almost there. You just have to fix the list comprehension "syntax":
d = [num for num in d if num]
Note: This is possible because Python treats 0 as False.
Try any of these:
d = [x for x in d if x != 0]
d = filter(lambda x: x != 0, d)
d = [x for x in d if x]