Package provides Python-style list comprehensions for R. List comprehension expressions use usual loops (for
, while
and repeat
) and usual if
as list producers. Syntax is very similar to Python. The difference is that returned value should be at the end of the loop body.
There are three main functions:
to_list
converts usual R loops expressions to list producers. Expression should be started with for
, while
or repeat
. You can iterate over multiple lists if you provide several loop variables in backticks. See examples.to_vec
is the same as to_list
but return vector. See examples.alter
return the same type as its argument but with modified elements. It is useful for altering existing data.frames or lists. See examples.Rather unpractical example - squares of even numbers:
library(comprehenr)
to_vec(for(i in 1:10) if(i %% 2==0) i*i)
#> [1] 4 16 36 64 100
Pythagorean triples:
to_list(for (x in 1:20) for (y in x:20) for (z in y:20) if (x^2 + y^2 == z^2) c(x, y, z))
#> [[1]]
#> [1] 3 4 5
#>
#> [[2]]
#> [1] 5 12 13
#>
#> [[3]]
#> [1] 6 8 10
#>
#> [[4]]
#> [1] 8 15 17
#>
#> [[5]]
#> [1] 9 12 15
#>
#> [[6]]
#> [1] 12 16 20
More examples:
= c("red", "green", "yellow", "blue")
colours = c("house", "car", "tree")
things to_vec(for(x in colours) for(y in things) paste(x, y))
#> [1] "red house" "red car" "red tree" "green house" "green car"
#> [6] "green tree" "yellow house" "yellow car" "yellow tree" "blue house"
#> [11] "blue car" "blue tree"
# prime numbers
= to_vec(for (i in 2:7) for (j in seq(i*2, 99, i)) j)
noprimes = to_vec(for (x in 2:99) if(!x %in% noprimes) x)
primes
primes#> [1] 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
You can iterate over multiple lists if you provide several loop variables in backticks:
to_vec(for(`i, j` in numerate(letters)) if(i %% 2==0) paste(i, j))
#> [1] "2 b" "4 d" "6 f" "8 h" "10 j" "12 l" "14 n" "16 p" "18 r" "20 t"
#> [11] "22 v" "24 x" "26 z"
set.seed(123)
= runif(20)
rand_sequence # gives only locally increasing values
to_vec(for(`i, j` in lag_list(rand_sequence)) if(j>i) j)
#> [1] 0.7883051 0.8830174 0.9404673 0.5281055 0.8924190 0.9568333 0.6775706
#> [8] 0.8998250 0.3279207 0.9545036
alter
examples:
data(iris)
# scale numeric variables
= alter(for(i in iris) if(is.numeric(i)) scale(i))
res str(res)
#> 'data.frame': 150 obs. of 5 variables:
#> $ Sepal.Length: num [1:150, 1] -0.898 -1.139 -1.381 -1.501 -1.018 ...
#> ..- attr(*, "scaled:center")= num 5.84
#> ..- attr(*, "scaled:scale")= num 0.828
#> $ Sepal.Width : num [1:150, 1] 1.0156 -0.1315 0.3273 0.0979 1.245 ...
#> ..- attr(*, "scaled:center")= num 3.06
#> ..- attr(*, "scaled:scale")= num 0.436
#> $ Petal.Length: num [1:150, 1] -1.34 -1.34 -1.39 -1.28 -1.34 ...
#> ..- attr(*, "scaled:center")= num 3.76
#> ..- attr(*, "scaled:scale")= num 1.77
#> $ Petal.Width : num [1:150, 1] -1.31 -1.31 -1.31 -1.31 -1.31 ...
#> ..- attr(*, "scaled:center")= num 1.2
#> ..- attr(*, "scaled:scale")= num 0.762
#> $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
# convert factors to characters
= alter(for(i in iris) if(is.factor(i)) as.character(i))
res str(res)
#> 'data.frame': 150 obs. of 5 variables:
#> $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#> $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#> $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#> $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#> $ Species : chr "setosa" "setosa" "setosa" "setosa" ...
# drop factors
= alter(for(i in iris) if(is.factor(i)) exclude())
res str(res)
#> 'data.frame': 150 obs. of 4 variables:
#> $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#> $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#> $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#> $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
# 'data' argument example
# specify which columns to map with a numeric vector of positions:
= alter(
res for(`i, value` in numerate(mtcars)) if(i %in% c(1, 4, 5)) as.character(value),
data = mtcars
)str(res)
#> 'data.frame': 32 obs. of 11 variables:
#> $ mpg : chr "21" "21" "22.8" "21.4" ...
#> $ cyl : num 6 6 4 6 8 6 8 4 4 6 ...
#> $ disp: num 160 160 108 258 360 ...
#> $ hp : chr "110" "110" "93" "110" ...
#> $ drat: chr "3.9" "3.9" "3.85" "3.08" ...
#> $ wt : num 2.62 2.88 2.32 3.21 3.44 ...
#> $ qsec: num 16.5 17 18.6 19.4 17 ...
#> $ vs : num 0 0 1 1 0 1 0 1 1 1 ...
#> $ am : num 1 1 1 0 0 0 0 0 0 0 ...
#> $ gear: num 4 4 4 3 3 3 3 4 4 4 ...
#> $ carb: num 4 4 1 1 2 1 4 2 2 4 ...
# or with a vector of names:
= alter(
res for(`name, value` in mark(mtcars)) if(name %in% c("cyl", "am")) as.character(value),
data = mtcars
)str(res)
#> 'data.frame': 32 obs. of 11 variables:
#> $ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
#> $ cyl : chr "6" "6" "4" "6" ...
#> $ disp: num 160 160 108 258 360 ...
#> $ hp : num 110 110 93 110 175 105 245 62 95 123 ...
#> $ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
#> $ wt : num 2.62 2.88 2.32 3.21 3.44 ...
#> $ qsec: num 16.5 17 18.6 19.4 17 ...
#> $ vs : num 0 0 1 1 0 1 0 1 1 1 ...
#> $ am : chr "1" "1" "1" "0" ...
#> $ gear: num 4 4 4 3 3 3 3 4 4 4 ...
#> $ carb: num 4 4 1 1 2 1 4 2 2 4 ...