Collect iterator as 2D array - julia

I'd like to create a 2D binary matrix, but Julia uses collect in an unexpected way and gives me a 3D matrix.
using Base.Iterators
collect(product(repeat([[0, 1]], 3)...))
Output
2×2×2 Array{Tuple{Int64,Int64,Int64},3}:
[:, :, 1] =
(0, 0, 0) (0, 1, 0)
(1, 0, 0) (1, 1, 0)
[:, :, 2] =
(0, 0, 1) (0, 1, 1)
(1, 0, 1) (1, 1, 1)
Iterating over the non-collected results however in the expected
for x in product(repeat([[0, 1]], 3)...)
println(x)
end
Output
(0, 0, 0)
(1, 0, 0)
(0, 1, 0)
(1, 1, 0)
(0, 0, 1)
(1, 0, 1)
(0, 1, 1)
(1, 1, 1)
How do I collect the tuples of the iterator as a 2D matrix?

Are you looking for something like this?
julia> X = product(repeat([[0, 1]], 3)...)
Base.Iterators.ProductIterator{Tuple{Array{Int64,1},Array{Int64,1},Array{Int64,1}}}(([0, 1], [0, 1], [0, 1]))
julia> X |> flatten |> collect |> x->reshape(x, (3,:))'
8×3 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
0 0 0
1 0 0
0 1 0
1 1 0
0 0 1
1 0 1
0 1 1
1 1 1
Note that in your particular case the rows of the final matrix just correspond to binary counting from 0 to 2^3-1 = 7. You could simply construct the result directly:
julia> reduce(vcat, digits.(0:7, base=2, pad=3)')
8×3 Array{Int64,2}:
0 0 0
1 0 0
0 1 0
1 1 0
0 0 1
1 0 1
0 1 1
1 1 1

Related

How do I create a column which has the value of the last time a condition was met in a separate row in R

I have data that looks like this:
d <- data.frame(Item = c(1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0)
I would like to create a column where the value is based on the last time a 0 was present in the row d$item. I don't really know how to get started with something like this in R.
Expected outcome is this:
f$recent <- c(NA, 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 1, 0, 1, 2, 3, 0)
Where each row is the the most recent observation of 0 (0 = on same row, 1 = previous row, etc.)
edit: Changed row to column, was posting before coffee. Also added expected result.
You can try rle + sequence
transform(
d,
recent = with(rle(Item), sequence(lengths)) * (Item != 0)
)
which gives
Item recent
1 1 1
2 0 0
3 0 0
4 1 1
5 1 2
6 1 3
7 1 4
8 1 5
9 1 6
10 0 0
11 0 0
12 1 1
13 0 0
14 1 1
15 1 2
16 1 3
17 0 0
You can do this, with sequence. It calculate the distance to the latest 1.
dif <- diff(c(which(d$Item == 1), length(d$Item) + 1))
sequence(dif, 0)
#[1] 0 1 2 0 0 0 0 0 0 1 2 0 1 0 0 0 1
Edit:
dif <- diff(c(1, which(d$Item != 1), length(d$Item) + 1))
sequence(dif, 0)
#[1] 0 0 0 1 2 3 4 5 6 0 0 1 0 1 2 3 0

How to produce a binary variable that takes value of 0 before the last non-zero value of another variable and 1 after it

Given the variable
a <- c(1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0)
I would like to mark the last non-zero value to produce a variable that takes value of 0 before the benchmark and 1 after it. In this case
b <- c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1)
A simple command that allows to mark the last non-zero value of a a column is
tail(which(a!=0),1)
But how to create the variable I need conditional on such benchmark? Any help would be much appreciated!
One option could be:
+(cumsum(a) == max(cumsum(a)))
[1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1
Or:
+(cumsum(a) == sum(a))
We can use which to get the position, index where a is 1, wrap with max to return the last position, create a logical vector with sequence of 'a' and coerce to binary (+)
+(seq_along(a) >= max(which(a == 1)))
#[1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1
Or use Position
+(seq_along(a) >= Position(function(x) x == 1, a, right = TRUE))

increase non sequential ones by one in a vector

I have a vector of 1s and 0s. I would like to replace the 1s with its "spot" in the vector.
For example I would like to change
x = c(1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1)
to
1 1 1 0 2 2 2 0 0 3 0 4
The numbers of 1s and 0s in a row can change.
Here is one way to do it...
x = c(1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1)
x[x==1] <- cumsum(c(x[1], diff(x)) == 1)[x==1]
x
[1] 1 1 1 0 2 2 2 0 0 3 0 4
Another way with rle
x * with(rle(x), rep(cumsum(values), lengths))
#[1] 1 1 1 0 2 2 2 0 0 3 0 4
We create a run-length sequence of x and repeat cumsum of values lengths time and multiply it by x so that 0's remain as 0's and only 1's are changed.
Here is an option with rle and inverse.rle
inverse.rle(within.list(rle(x), values[values==1] <- seq_along(values[values==1])))
#[1] 1 1 1 0 2 2 2 0 0 3 0 4
Or an option using rleid from data.table
library(data.table)
x1 <- rleid(x)
x1[x!= 0] <- rleid(x1[x!=0])
x1 * x
#[1] 1 1 1 0 2 2 2 0 0 3 0 4

Generating lists in R with patterns related to the entry number

Is there a smart way to generate a list like the one below in R using perhaps lapply() or other more extrapolable procedures?
ones = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
twos = c(1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
threes = c(1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0)
fours = c(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0)
fives = c(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
l = list(ones, twos, threes, fours)
[[1]]
[1] 1 1 1 1 1 1 1 1 1 1 1
[[2]]
[1] 1 0 1 0 1 0 1 0 1 0 1
[[3]]
[1] 1 0 0 1 0 0 1 0 0 1 0
[[4]]
[1] 1 0 0 0 1 0 0 0 1 0 0
These correspond to polynomials coefficients in generating functions for partitions.
The first list is for ones and so the counting is in steps of 1 integer at a time; hence the vector 1,1,1,1,1,1,1,... In the entry [[2]] we have the twos, and we are counting by 2's starting at 0, skipping the 1 (coded as 0). In [[3]] we are counting by 3's: zero, three, six, nine, etc.
A fairly straightforward way in base R is
lapply(seq(0L, 5L), function(i) rep(c(1L, integer(i)), length.out=11L))
[[1]]
[1] 1 1 1 1 1 1 1 1 1 1 1
[[2]]
[1] 1 0 1 0 1 0 1 0 1 0 1
[[3]]
[1] 1 0 0 1 0 0 1 0 0 1 0
[[4]]
[1] 1 0 0 0 1 0 0 0 1 0 0
[[5]]
[1] 1 0 0 0 0 1 0 0 0 0 1
seq(0L, 5L) produces the vector 0 through 5, an equivalent would be seq_len(5L)-1L, which is faster for creation of large vectors.
c(1L, integer(i)) produces the inner, repeated part of the 0-1 vectors, which rep repeats according to the desired length (here 11) using the length.out argument.
lapply and function(i) allow the number of 0s to increase as we loop through the vector.

Code onset from event occurrence

I have a vector that gives presence/absence of an event (insurgency in this case) over time, and I'd like to create another vector that gives onset of the event, i.e.:
occurrence <- c(1, 1, 0, 0, 1, 0, 0, 1, 1, 1)
onset <- c(0, 0, 0, 0, 1, 0, 0, 1, 0, 0)
The following loop will get what I need:
answer <- 0
for (t in 2:length(occurrence) {
answer[t] <- ifelse((occurrence[t]-occurrence[t-1])==1, 1, 0)
}
> answer
[1] 0 0 0 0 1 0 0 1 0 0
Is there an easier way of doing this?
Thanks.
Use pmax() and diff():
c(NA, pmax(0, diff(occurrence)))
[1] NA 0 0 0 1 0 0 1 0 0
This works because diff() calculates the difference between successive elements, resulting in 1 for every start. Then you need to remove the 0 and -1 values. pmax is a parallel version of max() and is handy to change all -1s to zero
diff(occurrence)
[1] 0 -1 0 1 -1 0 1 0 0
You can compare the prior time interval to the current one and chose those where 0 is followed by one with this code:
> c(0, as.numeric(occurrence[-length(occurrence)] == 0 & occurrence[-1]==1) )
[1] 0 0 0 0 1 0 0 1 0 0
(I padded it with a leading 0 because you didn't want leading occurrence:1's to be counted as new events.)

Resources