`map` equivalent to 2d list comprehension - julia

In 1d, I can use either of these:
[i for i in 1:5]
or
map(1:5) do i
i
end
both produce
[1,2,3,4,5]
Is there a way to use map in higher dimensions? e.g. to replicate
[x + y for x in 1:5,y in 10:13]
which produces
5×4 Array{Int64,2}:
11 12 13 14
12 13 14 15
13 14 15 16
14 15 16 17
15 16 17 18

You can do this:
julia> map(Iterators.product(1:3, 10:15)) do (x,y)
x+y
end
3×6 Array{Int64,2}:
11 12 13 14 15 16
12 13 14 15 16 17
13 14 15 16 17 18
The comprehension you wrote is I think just collect(x+y for (x,y) in Iterators.product(1:5, 10:13)), . Note the brackets (x,y), as the do function gets a tuple. Unlike x,y when it gets two arguments:
julia> map(1:3, 11:13) do x,y
x+y
end
3-element Array{Int64,1}:
12
14
16

This, of course, is not the map equivalent that you are looking for, but in some cases like this you can use broadcasting with a vector and a transposed vector:
x = 1:5
y = (10:13)'
x .+ y
At the REPL:
julia> (1:5) .+ (10:13)'
5×4 Array{Int64,2}:
11 12 13 14
12 13 14 15
13 14 15 16
14 15 16 17
15 16 17 18

Related

R Script to rearrange the elements of a vector by interleaving it

How to write an R-script to initialize a vector with integers, rearrange the elements by interleaving the
first half elements with the second half elements and store in the same vector without using pre-defined function and display the updated vector.
This sounds like a homework question, and it would be nice to see some effort on your own part, but it's pretty straightforward to do this in R.
Suppose your vector looks like this:
vec <- 1:20
vec
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Then you can just do:
c(t(cbind(vec[1:10], vec[11:20])))
#> [1] 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 19 10 20
This works by joining the two vectors into a 10 x 2 matrix, then transposing that matrix and turning it into a vector.
We may use matrix directly and concatenate
c(matrix(vec, nrow = 2, byrow = TRUE))
-output
[1] 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 19 10 20
data
vec <- 1:20
Or using mapply:
vec <- 1:20
c(mapply(\(x,y) c(x,y), vec[1:10], vec[11:20]))
#> [1] 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 19 10 20
We can try this using order + %%
> vec[order((seq_along(vec) - 1) %% (length(vec) / 2))]
[1] 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 19 10 20
Another way is to use rbind on the 2 halves of the vector, which creates a matrix with two rows. Then, we can then turn the matrix into a vector, which will go through column by column (i.e., 1, 11, 2, 12...). However, this will only work for even vectors.
vec <- 1:20
c(rbind(vec[1:10], vec[11:20]))
# [1] 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 19 10 20
So, for uneven vectors, we can use order, which will return the indices of the numbers in the two seq_along vectors.
vec2 <- 1:21
order(c(seq_along(vec2[1:10]),seq_along(vec2[11:21])))
# [1] 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 19 10 20 21

Selecting columns with only one character in R

Here is my data
df<-read.table(text="A1 A2 AA2 A3 APP3 AA4 A4
17 17 14 18 18 14 17
16 15 13 16 19 15 19
17 14 12 19 15 18 14
17 16 16 18 19 19 20
19 18 12 18 13 17 17
12 19 17 18 16 20 18
20 18 14 13 15 15 16
18 20 12 20 12 12 18
12 15 18 14 16 18 18",h=T)
I want to select columns that have only one A, i.e.,
A1 A2 A3 A4
17 17 18 17
16 15 16 19
17 14 19 14
17 16 18 20
19 18 18 17
12 19 18 18
20 18 13 16
18 20 20 18
12 15 14 18
I have used the following code:
df1<- df%>%
select(contains("A"))
but it gives me all As that start with A
Is it possible to get table 2? Thanks for your help.
You can use matches() with a regex pattern. A pattern for "contains exactly 1 'A'" would be this "^[^A]*A[^A]*$"
df %>% select(matches("^[^A]*A[^A]*$"))
# A1 A2 A3 A4
# 1 17 17 18 17
# 2 16 15 16 19
# 3 17 14 19 14
# 4 17 16 18 20
# ...
Based on comments, my best guess for what you want is columns where the name starts with a P and after the P contains only numbers:
# single P followed by numbers
df %>% select(matches("^P[0-9]+$"))
# single A followed by numbers
df %>% select(matches("^A[0-9]+$"))
# single capital letter followed by numbers
df %>% select(matches("^[A-Z][0-9]+$"))
If your not very comfortable with RegEx here's an alternative solution,
The first step is to create a function that counts the number of "A"s in a vector of strings, I will do this by creating a temporary vector of columns names with all the As removed and then subtracting the new number of characters from the original.
count_a<-function(vector,char){
vec2<-gsub("A","",vector, fixed=T)
numb_As<-nchar(vector)-nchar(vec2)
return(numb_As)
}
Once you have this function you simply apply it to the colnames of your dataset and then limit your data to the columns where the count is equal to one.
As<-count_a(colnames(df))
df[,As==1]
If you are not familiar with regular expressions, you can use a function of the popular package for analysing strings: stringr. With one line you get this:
library(stringr)
df[,str_count(names(df),'A')==1]

Multiplying odd numbers in a vector

I am a beginner in R and I need to multiply odd numbers (by two) of the following vector:
x<-c(1:20)
I tried with this:
x2<-c[lapply(x,"%%",2*2)==1]
But something is wrong.
For a vector like your example comprised of consecutive integers, we can use recycling
x * c(2,1)
##[1] 2 2 6 4 10 6 14 8 18 10 22 12 26 14 30 16 34 18 38 20
More generally, we can do
x * (x%%2 + 1L)
Using base r, we can try
ifelse(x %% 2 != 0, x * 2, x)
> [1] 2 2 6 4 10 6 14 8 18 10 22 12 26 14 30 16 34 18 38 20
We could find out the indices which are odd and multiply them by 2.
inds <- as.logical(x %% 2)
x[inds] <- x[inds] * 2
x
#[1] 2 2 6 4 10 6 14 8 18 10 22 12 26 14 30 16 34 18 38 20

Determine the number of elements that precede a number in two integers: R

I have two long doubles in R called m and n which consist of a variety of numbers:
m <- c(3,5,8,12,15,19,21,23,26,33,37,42,45,47,51,54,58,60)
n <- c(13,14,52,53,56,57)
I am trying to count the number of entries in m that precede each entry in n, plus 1.
For example, the first entry in n is 13. In m, there are 4 numbers 3,5,8,12, that precede 13, so I would like the program to return 5. For 14, the result that should be returned is also 5. For 52, there are 15 numbers in m preceding 52, so I would like the program to return 16.
Thank you!
We can use findInterval
findInterval(n, m) + 1
#[1] 5 5 16 16 17 17
m<-c(3,5,8,12,15,19,21,23,26,33,37,42,45,47,51,54,58,60)
n<-c(13,14,52,53,56,57)
sapply(n,function(x){sum(m<x)+1})
result:
[1] 5 5 16 16 17 17
cut(n,m,2:length(m))
[1] 5 5 16 16 17 17
Levels: 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
as.numeric(cut(n,m,2:length(m)))+1
[1] 5 5 16 16 17 17
as.numeric(as.character(cut(n,m,2:length(m))))
[1] 5 5 16 16 17 17

Cut function creates too many levels

I have a list of integers that represent years of education:
education= 12 14 17 15 12 19 16 12 16 14 12 18 12 13 18 18 10 13 12 18
22 16 13 22 12 15 12 16 18 18 18 20 18 16 13 12 16 13 18 20 20 20 14 18
18 12 18 16 20 18 14 16 19 12 12 11 13 13
I am trying to categorize the years into 3 different levels:
9-12
13-17
18+
I have tried to used the cut function:
edulevels=cut(education,c(9,12,13,17,18,22))
but it creates 2 additional levels for 12-13 and 17-18:
Levels: (9,12] (12,13] (13,17] (17,18] (18,22]
How do I get it to only create these three levels?
simplest solution
edulevels= cut(education,c(9,12.5,17.5,22), labels = c("9-12", "13-17", "18+"))
Intervals defined by the cut() function are closed on the right. To see what that means, try this:
cut(1:2, breaks=c(0,1,2))
# [1] (0,1] (1,2]
As you can see, the integer 1 gets included in the range (0,1], not in the range (1,2]. It doesn't get double-counted, and for any input value falling outside of the bins you define, cut() will return a value of NA.
When dealing with integer-valued data, I tend to set break points between the integers, just to avoid tripping myself up.
edulevels <- cut(education,
c(8.5, 12.5, 17.5, Inf),
labels=c('9-12','13-17','18+')
)

Resources