How do you populate an empty matrix with the values of another matrix?
The empty matrix:
> m1 <- matrix(ncol=8, nrow=8)
> rownames(m1) <- c('a','b','c','d','e','f','g','h')
> colnames(m1) <- c('a','b','c','d','e','f','g','h')
> m1
a b c d e f g h
a NA NA NA NA NA NA NA NA
b NA NA NA NA NA NA NA NA
c NA NA NA NA NA NA NA NA
d NA NA NA NA NA NA NA NA
e NA NA NA NA NA NA NA NA
f NA NA NA NA NA NA NA NA
g NA NA NA NA NA NA NA NA
h NA NA NA NA NA NA NA NA
The matrix with the values to populate the empty matrix:
> m2 <- matrix(ncol=4, nrow=4)
> rownames(m2) <- c('b','e','h','x')
> colnames(m2) <- c('b','e','h','x')
> m2[,'b'] <- c(1,2,3,1)
> m2[,'e'] <- c(2,1,1,5)
> m2[,'h'] <- c(3,1,3,5)
> m2[,'x'] <- c(1,5,5,1)
> m2
b e h x
b 1 2 3 1
e 2 1 1 5
h 3 1 3 5
x 1 5 5 1
How do you merge the two matrixes to get this result:
a b c d e f g h
a NA NA NA NA NA NA NA NA
b NA 1 NA NA 2 NA NA 3
c NA NA NA NA NA NA NA NA
d NA NA NA NA NA NA NA NA
e NA 2 NA NA 1 NA NA 1
f NA NA NA NA NA NA NA NA
g NA NA NA NA NA NA NA NA
h NA 3 NA NA 1 NA NA 3
Edit: added row/col x in m2, which is not in m1
Find the column (row) names that both matrices have in common
cols <- colnames(m1)[colnames(m1) %in% colnames(m2)]
rows <- rownames(m1)[rownames(m1) %in% rownames(m2)]
Then assign the appropriate values from m2 to m1
m1[rows, cols] <- m2[rows, cols]
m1
# a b c d e f g h
#a NA NA NA NA NA NA NA NA
#b NA 1 NA NA 2 NA NA 3
#c NA NA NA NA NA NA NA NA
#d NA NA NA NA NA NA NA NA
#e NA 2 NA NA 1 NA NA 1
#f NA NA NA NA NA NA NA NA
#g NA NA NA NA NA NA NA NA
#h NA 3 NA NA 1 NA NA 3
Related
I have a dataframe with variable number of F, F can go from 1 to X
df1 <- data.frame(F1=c(1,5,NA,9),F2=c(2,5,"a",NA),F3=c(1,NA,"o",NA))
df1
F1 F2 F3
1 1 2 1
2 5 5 <NA>
3 NA a o
4 9 <NA> <NA>
but I need my data frame to have certain amount of columns, lets say 30.
If X is smaller then 30 then I need the amount of columns needed (with NA values) to be added to make 30 columns.
> df1
F1 F2 F3 F4......F30
1 1 2 1 <NA>.....<NA>
2 5 5 <NA> <NA>.....<NA>
3 NA a o <NA>.....<NA>
4 9 <NA> <NA> <NA>.....<NA>
Thanks!
We may use setdiff on the names of the dataset and assign those to NA
df1[setdiff(paste0("F", 1:30), names(df1))] <- NA
-output
> df1
F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30
1 1 2 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
2 5 5 <NA> NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
3 NA a o NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
4 9 <NA> <NA> NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
I think it can first create an empty data.frame with X or 30 columns, than replace selected columns with your data.frame.
libary(dplyr)
library(purrr)
df1<-tibble(map_dfc(1:30, ~rep(NA, nrow(df1)))) %>%
set_names(~paste0("F", 1:30)) %>%
coalesce(., df1)
I've got a dataset like the following.
df <- read.table(header=TRUE, text="
T_A_01_F_1 T_A_02_F_1 T_A_03_F_1 T_A_01_F_2 T_A_02_F_2 T_A_03_F_2 T_A_01_U_1 T_A_02_U_1 T_A_03_U_1 T_A_01_U_2 T_A_02_U_2 T_A_03_U_2 T_B_01_F_1 T_B_02_F_1 T_B_03_F_1 T_B_01_F_2 T_B_02_F_2 T_B_03_F_2 T_B_01_U_1 T_B_02_U_1 T_B_03_U_1 T_B_01_U_2 T_B_02_U_2 T_B_03_U_2
1 2 3 NA NA NA 2 2 2 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
2 2 5 NA NA NA 1 3 3 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
5 3 3 NA NA NA 2 1 2 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
2 4 5 NA NA NA 6 3 4 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
NA NA NA 4 3 5 NA NA NA 4 3 2 NA NA NA NA NA NA NA NA NA NA NA NA
NA NA NA 4 4 5 NA NA NA 2 1 1 NA NA NA NA NA NA NA NA NA NA NA NA
NA NA NA 3 1 4 NA NA NA 2 1 7 NA NA NA NA NA NA NA NA NA NA NA NA
NA NA NA 2 1 6 NA NA NA 3 3 6 NA NA NA NA NA NA NA NA NA NA NA NA
NA NA NA NA NA NA NA NA NA NA NA NA 1 1 1 NA NA NA 2 3 1 NA NA NA
NA NA NA NA NA NA NA NA NA NA NA NA 2 1 1 NA NA NA 3 2 2 NA NA NA
NA NA NA NA NA NA NA NA NA NA NA NA 3 2 1 NA NA NA 4 2 1 NA NA NA
NA NA NA NA NA NA NA NA NA NA NA NA 2 5 4 NA NA NA 6 1 4 NA NA NA
NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA 3 2 2 NA NA NA 1 2 5
NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA 5 4 4 NA NA NA 3 3 5
NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA 5 4 4 NA NA NA 1 3 5
NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA 5 1 3 NA NA NA 7 5 1
")
In this case its a 2x2x2 mixed design with "T" being the variable of interest with 3 items, "A" and "B" the between factor, "F" and "U" the within factor and "1" and "2" the between factor. I'd like to reduce the dataset, so that I can compute a cronbachs alpha.
As every Person either got A or B and either 1 or 2 I'd like to combine those items, so that I only have the items T_01_F, T_01_U, T_02_F, T_02_U, T_03_F, T_03_U
I could do this by hand, but does somebody know a quick command with which I could do that?
Thank you so much in advance!!
best, Nash
Perhaps a combination of pivot_longer() and separate() might work since all your column names appear to share the same structure.
library(tidyverse)
df<- df %>%
rownames_to_column() %>%
pivot_longer(cols = T_A_01_F_1:T_B_03_U_2) %>%
separate(col = name, sep = "_", into = c("t", "a_b", "number" , "within", "between"))
I have a vector:
x <- c(1,2,3,4)
I would like to add 23 NA elements before each element of x
Maybe like this?
c(sapply(x, function(x) c(rep(NA,23),x)))
We can do this with vectorization
replace(rep(NA, 23*length(x) + length(x)), rep(c(FALSE, TRUE), c(23, 1)), x)
#[1] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
#[43] NA NA NA NA NA 2 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA 3 NA NA NA NA NA NA NA NA NA NA NA NA
#[85] NA NA NA NA NA NA NA NA NA NA NA 4
Or another option is to create a matrix, replace the last row with 'x' and convert it to vector
m1 <- matrix(rep(rep(NA, 24), length(x)), nrow = length(x))
m1[,24] <- x
c(t(m1))
I would like to apply a function (will be a custom function, but for simplicity I will say it is mean) to subgroups defined by combinations of factors. I have 20 factors, but I would like to consider, say, subgroups defined by all combinations of 1,2,3,...,k of the factors.
Here is an example for k=3
N = 100
test_data <- data.frame( factorA = factor(sample(1:4, replace = TRUE, size = N)), factorB = factor(sample(1:2, replace = TRUE, size = N)), factorC = factor(sample(1:2, replace = TRUE, size = N)), var = rnorm(n = N))
#1-way subsets
mean(test_data$var[test_data$factorA == "1"])
mean(test_data$var[test_data$factorA == "2"])
mean(test_data$var[test_data$factorA == "3"])
mean(test_data$var[test_data$factorA == "4"])
mean(test_data$var[test_data$factorB == "1"])
#and so forth...
#2-way subsets
mean(test_data$var[test_data$factorA == "1" & test_data$factorB == "1" ])
mean(test_data$var[test_data$factorA == "1" & test_data$factorB == "2" ])
mean(test_data$var[test_data$factorA == "1" & test_data$factorC == "1" ])
#and so forth...
#3-way subsets
mean(test_data$var[test_data$factorA == "1" & test_data$factorB == "1" & test_data$factorC == "1" ])
mean(test_data$var[test_data$factorA == "1" & test_data$factorB == "1" & test_data$factorC == "2" ])
#and so forth...
For each combinations of k factors, compute the mean of var for all combinations of levels for these k factors. It would be best if the output is then labeled the given combination of factors/levels that defines the subset.
It seems that expand.grid and/or combn should be useful, but not sure how to use them in this situation.
To calculate the mean of var for all combinations of all three factors you can use the data.table by argument:
library(data.table)
N = 100
test_data <- data.frame(factorA = factor(sample(1:4, replace = TRUE, size = N)),
factorB = factor(sample(1:2, replace = TRUE, size = N)),
factorC = factor(sample(1:2, replace = TRUE, size = N)), var = rnorm(n = N))
setDT(test_data)
test_data[, .(mean_var = mean(var, na.rm = TRUE)),
by = .(factorA, factorB, factorC)]
Which gives this output:
factorA factorB factorC mean_var
1: 1 1 1 -0.304218613
2: 1 1 2 -0.122405096
3: 1 2 1 0.532219871
4: 1 2 2 -0.679400706
5: 2 1 1 0.006901209
6: 2 1 2 0.605850466
7: 2 2 1 -0.083305497
8: 2 2 2 -0.408660971
9: 3 1 1 -0.362234218
10: 3 1 2 -0.368472511
11: 3 2 1 0.243274183
12: 3 2 2 0.119927615
13: 4 1 1 -0.517337915
14: 4 1 2 -0.790908511
15: 4 2 1 -0.077665828
16: 4 2 2 -0.295695277
Updated with example data containing 20 factor columns (with two to four levels each). All possible combinations of three factors (i.e. columns) are generated (6480) and for each combination the mean_var for each unique combination of factor levels is calculated:
library(data.table)
# Generate example data
N = 100
dt <- dcast(rbindlist(lapply(seq(1:20), function(x) {
dt_tmp <- data.table(id = 1:N, factor = paste0("factor", LETTERS[x]),
value = sample(1:sample(2:4, 1), replace = TRUE, size = N))
})), id~factor)[, ":="(var = rnorm(n = N), id = NULL)]
# Generate all combinations of three out of the 20 factors (20*19*18 = 6840)
factors <- colnames(dt[, 1:20])
tests <- CJ(k1 = factors, k2 = factors, k3 = factors)[k1 != k2 & k1 != k3 & k2 != k3]
# Iterate over every row of tests and calculate mean_var for each unique
# combination of the three factors (this takes time - output ~ 170000 rows)
dt_out <- rbindlist(lapply(seq(1:nrow(tests)), function(x) {
dt[, .(mean_var = mean(var, na.rm = TRUE)),
by = c(tests[x, k1], tests[x, k2], tests[x, k3])]
}), use.names = TRUE, fill = TRUE)
The output looks like this:
> head(out_dt, 30)
factorA factorB factorC mean_var factorD factorE factorF factorG factorH factorI factorJ factorK factorL factorM factorN factorO factorP factorQ factorR factorS factorT
1: 1 2 3 -0.595391823 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
2: 2 1 1 -0.049915238 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
3: 2 2 4 0.087206182 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
4: 2 1 2 0.010622079 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
5: 1 2 1 0.277414685 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
6: 1 1 3 0.366482963 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
7: 2 2 3 0.017438655 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
8: 2 2 1 -1.116071505 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
9: 2 1 4 1.371340706 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
10: 2 2 2 0.045354904 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
11: 1 2 2 0.644926008 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
12: 1 2 4 -0.121767568 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
13: 1 1 2 0.261070274 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
14: 2 1 3 -0.506061865 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
15: 1 1 4 -0.075228598 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
16: 1 1 1 0.333514316 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
17: 1 2 NA -0.185980008 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
18: 2 1 NA -0.113793548 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
19: 2 2 NA 0.015100176 2 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
20: 1 2 NA 0.484182038 2 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
21: 1 1 NA -0.123811140 2 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
22: 1 1 NA 0.543852715 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
23: 2 2 NA -0.267626769 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
24: 2 1 NA 0.133316773 2 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
25: 1 2 NA 0.538964320 NA 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
26: 2 1 NA 0.006298113 NA 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
27: 2 2 NA 0.010152043 NA 2 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
28: 2 1 NA 0.011377912 NA 2 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
29: 1 1 NA 0.504610954 NA 2 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
30: 2 2 NA -0.311834384 NA 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
factorA factorB factorC mean_var factorD factorE factorF factorG factorH factorI factorJ factorK factorL factorM factorN factorO factorP factorQ factorR factorS factorT
I have a function that takes as input a dataframe with certain columns
columns =['a', 'b',...,'z']
Now I have a dataframe DF with only few of these columns DF_columns = ['f', 'u', 'z']
How can I create a dataframe that has all the columns with value NA if the columns are not in DF and that coincides with DF on the columns ['f', 'u', 'z']
Example:
d = data.frame('g'=c(1,2,3), 's' = c(4,2,3))
columns = letters[1:21]
columns
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t"
[21] "u"
> d
g s
1 1 4
2 2 2
3 3 3
>
x.or.na <- function(x, df) if (x %in% names(df)) df[[x]] else NA
as.data.frame(Map(x.or.na, columns, list(d)))
set.seed(42)
DF <- setNames(as.data.frame(matrix(sample(1:15, 15, replace=TRUE), ncol=3)), c('f', 'u', 'z') )
DF
# f u z
#1 14 8 7
#2 15 12 11
#3 5 3 15
#4 13 10 4
#5 10 11 7
res <- do.call(`data.frame`,lapply(split(letters[4:26], letters[4:26]),
function(x){x1 <- match(x, colnames(DF)); if(!is.na(x1)) DF[,x1] else NA}))
res
# d e f g h i j k l m n o p q r s t u v w x y z
#1 NA NA 14 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 8 NA NA NA NA 7
#2 NA NA 15 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 12 NA NA NA NA 11
#3 NA NA 5 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 3 NA NA NA NA 15
#4 NA NA 13 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 10 NA NA NA NA 4
#5 NA NA 10 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 11 NA NA NA NA 7
Using dplyr
library(dplyr)
DF %>%
do({x1 <-data.frame(., setNames(as.list(rep(NA, sum(!letters[4:26] %in% names(DF)))),
setdiff(letters[4:26], names(DF))))
x1[,order(colnames(x1))] })
# d e f g h i j k l m n o p q r s t u v w x y z
#1 NA NA 14 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 8 NA NA NA NA 7
#2 NA NA 15 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 12 NA NA NA NA 11
#3 NA NA 5 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 3 NA NA NA NA 15
#4 NA NA 13 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 10 NA NA NA NA 4
#5 NA NA 10 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 11 NA NA NA NA 7
This is quite easy (in terms of syntax) and efficient (in terms of speed) using the data.table package:
require(data.table) ## 1.9.2+
setDT(d)[, setdiff(columns, names(d)) := NA] ## (1)
setcolorder(d, columns) ## (2)
setDF(d) ## (3)
setDT converts d to a data.table, after which we use the := operator to create new columns by reference. There are many ways to use :=, but highlighted here is the use case LHS := RHS. Here LHS is a vector of column names and RHS is the value. NA is provided only once on the RHS, which gets automatically recycled for all other columns. Note that NA by default is logical type in R.
If required you can reorder the columns of d in the same order as columns using setcolorder.
Again, if necessary, you can convert the data.table back to a data.frame, using the function setDF, which again modifies the object by reference. But it's available in the development version v1.9.3 only for now.
Here are a few methods and their timings.
createDF1 <- function(colVec, data)
{
m <- matrix(, nrow = nrow(data), ncol = length(colVec),
dimnames = list(NULL, colVec))
m[, names(data)] <- as.matrix(data)
data.frame(apply(m, 2, as.numeric))
}
createDF2 <- function(colVec, data)
{
rr <- setNames(rep(list(rep(NA_integer_, nrow(data))), length(colVec)), .
nm = colVec)
rr[match(names(data), colVec)] <- data
as.data.frame(rr)
}
createDF3 <- function(colVec, data)
{
rr <- setNames(replicate(length(colVec),
list(rep(NA_integer_, nrow(data)))),
nm = colVec)
rr[match(names(d), colVec)] <- data
as.data.frame(rr)
}
Create a 3,000,000 x 3 data frame to test on:
columns <- letters[1:21]
d <- data.frame(g = 1:3e6L, s = 1:3e6L, j = 1:3e6L)
Run some tests:
system.time({ createDF1(columns, d) })
# user system elapsed
# 5.022 1.023 6.054
system.time({ createDF2(columns, d) })
# user system elapsed
# 0.007 0.004 0.011
system.time({ createDF3(columns, d) })
# user system elapsed
# 0.105 0.077 0.183
Of these three, it looks like rep(list(rep(NA_integer_, nrow(data))), length(columns)) is the way to go, and replace values from that.
Setup:
set.seed(1)
DF_all <- setNames(data.frame(matrix(rnorm(5*26), nrow=5, ncol=26)), letters)
DF <- DF_all[, c('f','u','z')]
Create a new empty dataframe and populate with your columns:
DF2 <- setNames(data.frame(matrix(nrow=5, ncol=26)), letters)
DF2[, c('f','u','z')] <- DF[, c('f','u','z')]
Result:
> DF2
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
1 NA NA NA NA NA -0.05612874 NA NA NA NA NA NA NA NA NA NA NA NA NA NA -0.62036668 NA NA NA NA 0.71266631
2 NA NA NA NA NA -0.15579551 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 0.04211587 NA NA NA NA -0.07356440
3 NA NA NA NA NA -1.47075238 NA NA NA NA NA NA NA NA NA NA NA NA NA NA -0.91092165 NA NA NA NA -0.03763417
4 NA NA NA NA NA -0.47815006 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 0.15802877 NA NA NA NA -0.68166048
5 NA NA NA NA NA 0.41794156 NA NA NA NA NA NA NA NA NA NA NA NA NA NA -0.65458464 NA NA NA NA -0.32427027
[<- could be used to fill up the missing columns with NA.
`[<-`(d,, setdiff(columns, names(d)), NA)[columns]
#`[<-`(d,, columns[!columns %in% names(d)], NA)[columns] #Alternative
# a b c d e f g h i j k l m n o p q r s t u
#1 NA NA NA NA NA NA 1 NA NA NA NA NA NA NA NA NA NA NA 4 NA NA
#2 NA NA NA NA NA NA 2 NA NA NA NA NA NA NA NA NA NA NA 2 NA NA
#3 NA NA NA NA NA NA 3 NA NA NA NA NA NA NA NA NA NA NA 3 NA NA
Or directly adding the missing columns to the original data.frame
d[columns[!columns %in% names(d)]] <- NA
d[columns]
# a b c d e f g h i j k l m n o p q r s t u
#1 NA NA NA NA NA NA 1 NA NA NA NA NA NA NA NA NA NA NA 4 NA NA
#2 NA NA NA NA NA NA 2 NA NA NA NA NA NA NA NA NA NA NA 2 NA NA
#3 NA NA NA NA NA NA 3 NA NA NA NA NA NA NA NA NA NA NA 3 NA NA
Or in a function:
f <- function(DF, COL) {
d[columns[!columns %in% names(d)]] <- NA
d[columns]
}
f(d, columns)
# a b c d e f g h i j k l m n o p q r s t u
#1 NA NA NA NA NA NA 1 NA NA NA NA NA NA NA NA NA NA NA 4 NA NA
#2 NA NA NA NA NA NA 2 NA NA NA NA NA NA NA NA NA NA NA 2 NA NA
#3 NA NA NA NA NA NA 3 NA NA NA NA NA NA NA NA NA NA NA 3 NA NA
Data
d <- data.frame('g'=c(1,2,3), 's' = c(4,2,3))
columns <- letters[1:21]