How do I refer to variable in func argument when same is used in foreach - rebol

How can I refer to date as argument in f within the foreach loop if date is also used as block element var ? Am I obliged to rename my date var ?
f: func[data [block!] date [date!]][
foreach [date o h l c v] data [
]
]

A: simple, compose is your best friend.
f: func[data [block!] date [date!]][
foreach [date str] data compose [
print (date)
print date
]
]
>> f [2010-09-01 "first of sept" 2010-10-01 "first of october"] now
7-Sep-2010/21:19:05-4:00
1-Sep-2010
7-Sep-2010/21:19:05-4:00
1-Oct-2010

You need to either change the parameter name from date or assign it to a local variable.

You can access the date argument inside the foreach loop by binding the 'date word from the function specification to the data argument:
>> f: func[data [block!] date [date!]][
[ foreach [date o h l c v] data [
[ print last reduce bind find first :f 'date 'data
[ print date
[ ]
[ ]
>> f [1-1-10 1 2 3 4 5 2-1-10 1 2 3 4 5] 8-9-10
8-Sep-2010
1-Jan-2010
8-Sep-2010
2-Jan-2010
It makes the code very difficult to read though. I think it would be better to assign the date argument to a local variable inside the function as Graham suggested.
>> f: func [data [block!] date [date!] /local the-date ][
[ the-date: :date
[ foreach [date o h l c v] data [
[ print the-date
[ print date
[ ]
[ ]
>> f [1-1-10 1 2 3 4 5 2-1-10 1 2 3 4 5] 8-9-10
8-Sep-2010
1-Jan-2010
8-Sep-2010
2-Jan-2010

Related

Assign variable to breed based on percentage

I'm trying to assign my breed called "evacuees" their age based on a percentage. The number of evacuees is based on a slider in the interface, meaning that I don't have a fixed population to work with. They have two properties, "sex" and "age", which are both based on a percentage.
I assigned the sex the following way:
let women n-of (count evacuees * 0.513) evacuees
ask women [set sex "female"]
ask evacuees [if not member? self women [set sex "male"]]
That works fine if you only have two categories. But when you have more than two (I have five different age groups) this doesn't work anymore. I tried to still use n-of but with if conditions, so that the agents are not drawn from the whole pool of the evacuees, but only the ones that haven't had an age assigned yet:
set men-0-14 n-of (count evacuees with [sex = "male"] * 0.11) evacuees
ask men-0-14 [set age "0-14"]
ask evacuees [
if not member? self men-0-14 [
set men-15-19 n-of (count evacuees with [sex = "male"] * 0.04) evacuees with [sex = "male" AND
not member? self men-0-14]
]
]
ask men-15-19 [set age "15-19"]
ask evacuees [
if not member? self men-0-14 AND not member? self men-15-19 [
set men-20-39 n-of (count evacuees with [sex = "male"] * 0.26) evacuees with [sex = "male" AND
not member? self men-0-14 AND not member? self men-15-19]
]
]
ask men-20-39 [set age "20-39"]
... and so on for all five categories. But in the end I will still have some male evacuees that don't have an age assigned. I think that might be because of the usage of n-of, that it always draws from the whole evacuees, even if using if-conditions. Or it might be a problem of scheduling in NetLogo, that all of the evacuees are still part of the pool because the age group gets assigned definitely when the procedure is finished.
Is there another way to create the five agentsets that have an age assigned based on a certain percentage?
Here's a generic solution that lets you assign sexes and age-groups in a nice table format in setup, then applies those percentages of each group to however many turtles you have, and reports on the results. It seems to work.
globals [ sex-percentages age-percentages ]
turtles-own [ sex_code sex_label age_code age_label ]
to setup
clear-all
let delist []
;; assume the percent lists are % of total population desired, code, and label
set sex-percentages [
[0.4 "M" "male" ]
[0.6 "F" "female" ]
]
set age-percentages [
[0.2 1 "under 10" ]
[0.5 2 "11-30" ]
[0.2 3 "31-50" ]
[0.1 4 "over 50 " ]
]
create-turtles ( 100 + random 20) [set size 2 set shape "person" setxy random-xcor random-ycor
set delist assign sex-percentages
set sex_code first delist
set sex_label last delist
set label sex_code
set delist assign age-percentages
set age_code first delist
set age_label last delist
set label age_code
]
report-sexes
report-ages
reset-ticks
end
to report-sexes
let sexlist []
let n count turtles
ask turtles [ set sexlist lput sex_code sexlist ]
let sexes sort remove-duplicates sexlist
foreach sexes
[ x -> let thiscount length filter [ i -> i = x ] sexlist
let pct ( 100 * thiscount / n )
print (word x " has " thiscount " of " n " = " precision pct 3 " percent" )
]
print "Requested: "
foreach age-percentages [ x -> print x ]
end
to report-ages
let agelist []
let n count turtles
ask turtles [ set agelist lput age_code agelist ]
let ages sort remove-duplicates agelist
foreach ages
[ x -> let thiscount length filter [ i -> i = x ] agelist
let pct ( 100 * thiscount / n )
print (word x " has " thiscount " of " n " = " precision pct 3 " percent" )
]
print "Requested: "
foreach age-percentages [ x -> print x ]
end
to-report assign [ distribution ]
let chooser random-float 1 ;; returns value in range [0, 0.99999]
let assigned false
let running_sum 0
;; set default values in case the code isn't working correctly
let decode "?"
let delabel "?"
foreach distribution
[
x ->
let group-pct item 0 x
let group-code item 1 x
let group-label item 2 x
set running_sum ( running_sum + group-pct)
if ( running_sum > 1) [ print "ERROR - total is over 100%" stop]
if (( assigned = false ) and ( chooser <= running_sum))
[ set decode group-code
set delabel group-label
set assigned true
]
]
report (list decode delabel )
end

Inverse of `split` function: `join` a string using a delimeter

IN Red and Rebol(3), you can use the split function to split a string into a list of items:
>> items: split {1, 2, 3, 4} {,}
== ["1" " 2" " 3" " 4"]
What is the corresponding inverse function to join a list of items into a string? It should work similar to the following:
>> join items {, }
== "1, 2, 3, 4"
There's no inbuild function yet, you have to implement it yourself:
>> join: function [series delimiter][length: either char? delimiter [1][length? delimiter] out: collect/into [foreach value series [keep rejoin [value delimiter]]] copy {} remove/part skip tail out negate length length out]
== func [series delimiter /local length out value][length: either char? delimiter [1] [length? delimiter] out: collect/into [foreach value series [keep rejoin [value delimiter]]] copy "" remove/part skip tail out negate length length out]
>> join [1 2 3] #","
== "1,2,3"
>> join [1 2 3] {, }
== "1, 2, 3"
per request, here is the function split into more lines:
join: function [
series
delimiter
][
length: either char? delimiter [1][length? delimiter]
out: collect/into [
foreach value series [keep rejoin [value delimiter]]
] copy {}
remove/part skip tail out negate length length
out
]
There is an old modification of rejoin doing that
rejoin: func [
"Reduces and joins a block of values - allows /with refinement."
block [block!] "Values to reduce and join"
/with join-thing "Value to place in between each element"
][
block: reduce block
if with [
while [not tail? block: next block][
insert block join-thing
block: next block
]
block: head block
]
append either series? first block [
copy first block
] [
form first block
]
next block
]
call it like this rejoin/with [..] delimiter
But I am pretty sure, there are other, even older solutions.
Following function works:
myjoin: function[blk[block!] delim [string!]][
outstr: ""
repeat i ((length? blk) - 1)[
append outstr blk/1
append outstr delim
blk: next blk ]
append outstr blk ]
probe myjoin ["A" "B" "C" "D" "E"] ", "
Output:
"A, B, C, D, E"

need to all extract the content inside the brackets in pandas dataframe

I need to extract only the content inside the brackets in pandas dataframe. I tried using str.exratct() but its not working . I need help with the extraction
DATA: ( IS IN DATA FRAME, This is a sample data from one row )
By:Chen TX (Chen Tianxu)[ 1 ] ; Tribbitt MA (Tribbitt Mark A.)[ 2 ] ; Yang Y (Yang Yi)[ 3 ] ; Li XM (Li Xiaomei)[ 4 ]
You can use the regular expression:
import pandas as pd
import re
dataset = pd.DataFrame([{'DATA': 'By:Chen TX (Chen Tianxu)[ 1 ] ; Tribbitt MA (Tribbitt Mark A.)[ 2 ] ; Yang Y (Yang Yi)[ 3 ] ; Li XM (Li Xiaomei)[ 4 ]'}])
print(dataset)
Dataframe is:
DATA
0 By:Chen TX (Chen Tianxu)[ 1 ] ; Tribbitt MA (Tribbitt Mark A.)[ 2 ] ; Yang Y (Yang Yi)[ 3 ] ; Li XM (Li Xiaomei)[ 4 ]
Then, using regular expression with lambda function, such that you extract names and save it to different column named names:
# regular expression from: https://stackoverflow.com/a/31343831/5916727
dataset['names'] = dataset['DATA'].apply(lambda x: re.findall('\((.*?)\)',x))
print(dataset['names'])
Output of names column would be:
0 [Chen Tianxu, Tribbitt Mark A., Yang Yi, Li Xiaomei]

How do I convert set-words in a block to words

I want to convert a block from block: [ a: 1 b: 2 ] to [a 1 b 2].
Is there an easier way of than this?
map-each word block [ either set-word? word [ to-word word ] [ word ] ]
Keeping it simple:
>> block: [a: 1 b: 2]
== [a: 1 b: 2]
>> forskip block 2 [block/1: to word! block/1]
== b
>> block
== [a 1 b 2]
I had same problem so I wrote this function. Maybe there's some simpler solution I do not know of.
flat-body-of: function [
"Change all set-words to words"
object [object! map!]
][
parse body: body-of object [
any [
change [set key set-word! (key: to word! key)] key
| skip
]
]
body
]
These'd create new blocks, but are fairly concise. For known set-word/value pairs:
collect [foreach [word val] block [keep to word! word keep val]]
Otherwise, you can use 'either as in your case:
collect [foreach val block [keep either set-word? val [to word! val][val]]]
I'd suggest that your map-each is itself fairly concise also.
I like DocKimbel's answer, but for the sake of another alternative...
for i 1 length? block 2 [poke block i to word! pick block i]
Answer from Graham Chiu:
In R2 you can do this:
>> to block! form [ a: 1 b: 2 c: 3]
== [a 1 b 2 c 3]
Or using PARSE:
block: [ a: 1 b: 2 ]
parse block [some [m: set-word! (change m to-word first m) any-type!]]

I need to generate 50 Millions Rows csv file with random data: how to optimize this program?

The program below can generate random data according to some specs (example here is for 2 columns)
It works with a few hundred of thousand lines on my PC (should depend on RAM). I need to scale to dozen of millions row.
How can I optimize the program to write directly to disk ? Subsidiarily how can I "cache" the parsing rule execution as it is always the same pattern repeated 50 Millions times ?
Note: to use the program below, just type generate-blocks and save-blocks output will be db.txt
Rebol[]
specs: [
[3 digits 4 digits 4 letters]
[2 letters 2 digits]
]
;====================================================================================================================
digits: charset "0123456789"
letters: charset "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
separator: charset ";"
block-letters: [A B C D E F G H I J K L M N O P Q R S T U V W X Y Z]
blocks: copy []
generate-row: func[][
Foreach spec specs [
rule: [
any [
[
set times integer! [['digits (
repeat n times [
block: rejoin [block random 9]
]
)
|
'letters (repeat n times [
block: rejoin [ block to-string pick block-letters random 24]
]
)
]
|
[
'letters (repeat n times [block: rejoin [ block to-string pick block-letters random 24]
]
)
|
'digits (repeat n times [block: rejoin [block random 9]]
)
]
]
|
{"} any separator {"}
]
]
to end
]
block: copy ""
parse spec rule
append blocks block
]
]
generate-blocks: func[m][
repeat num m [
generate-row
]
]
quote: func[string][
rejoin [{"} string {"}]
]
save-blocks: func[file][
if exists? to-rebol-file file [
answer: ask rejoin ["delete " file "? (Y/N): "]
if (answer = "Y") [
delete %db.txt
]
]
foreach [field1 field2] blocks [
write/lines/append %db.txt rejoin [quote field1 ";" quote field2]
]
]
Use open with /direct and /lines refinement to write directly to file without buffering the content:
file: open/direct/lines/write %myfile.txt
loop 1000 [
t: random "abcdefghi"
append file t
]
Close file
This will write 1000 random lines without buffering.
You can also prepare a block of lines (lets say 10000 rows) then write it directly to file, this will be faster than writing line-by-line.
file: open/direct/lines/write %myfile.txt
loop 100 [
b: copy []
loop 1000 [append b random "abcdef"]
append file b
]
close file
this will be much faster, 100000 rows less than a second.
Hope this will help.
Note that, you can change the number 100 and 1000 according to your needs an memory of your pc, and use b: make block! 1000 instead of b: copy [], it will be faster.