Pass String as Name of Attached Data Column Name

Pass string as name of attached data column name

Summary

In light of the various changes to the detail of the question, here are two solutions to the problem that can be phrased as:

Given

col_names <- c("Obj1$Var1", "Obj2$Var2")

how to return a data frame that would be the equivalent of

cbind(Obj1$Var1, Obj2$Var2)

?

The simplest solution would be

as.data.frame(sapply(col_names, function(x) eval(parse(text = x))))

but that uses parse() which shouldn't be relied on for things like this. An alternative, but somewhat longer solution is

get4 <- function(x, ...) {
fun <- function(text, ...) {
obj <- get(text[1], ...)
obj[[text[2]]]
}
sx <- strsplit(x, "\\$")
lx <- lapply(sx, fun, ...)
out <- do.call(cbind.data.frame, lx)
names(out) <- x
out
}

get4(col_names)

The second solution has advantages, despite being somewhat longer, in that it

  1. will work for data of different types as it works with a list and converts that to a data frame. The eval(parse(text = ....)) solution simplifies to an array first. Using lapply() instead of sapply() is an option that gets round this, but needs extra work to change the names of the resulting object.
  2. uses common function get() to grab the object with stated name, and basic subsetting syntax.
  3. doesn't use parse ;-)

Original Answer

The original Answer with greater detail continues below:

eval(parse(....)) will work

data1 <- data.frame(column1 = 1:10, column2 = letters[1:10])
txt <- "data1$column2"

> eval(parse(text = txt))
[1] a b c d e f g h i j
Levels: a b c d e f g h i j

As @texb mentions, this can trivially be extended to handle a vector of strings via (modified to return a data frame)

col_names <- c("iris$Sepal.Length", "iris$Sepal.Width")
as.data.frame(sapply(col_names, function(x) eval(parse(text = x))))

It may be more acceptable to use get but you'll have to do a bit of precessing, something along the lines of

get2 <- function(x, ...) {
sx <- strsplit(x, "\\$")[[1]]
obj <- get(sx[1], ...)
obj[[sx[2]]]
}

> get2(txt)
[1] a b c d e f g h i j
Levels: a b c d e f g h i j

iris example from OP's question

As @texb mentions, the eval(parse(text = ....)) version can trivially be extended to handle a vector of strings via (modified to return a data frame)

col_names <- c("iris$Sepal.Length", "iris$Sepal.Width")
as.data.frame(sapply(col_names, function(x) eval(parse(text = x))))

iris$Sepal.Length iris$Sepal.Width
1 5.1 3.5
2 4.9 3.0
3 4.7 3.2
4 4.6 3.1
5 5.0 3.6
6 5.4 3.9
....

Modifiying get2() is also possible to allow it to work on a vector of strings such as col_names. Here I loop over the first elements of sx to extract the object string (checking that there is only one unique object name), then I get that object and then subset it using the variable names (extracted using sapply(sx, `[`, 2))

get3 <- function(x, ...) {
sx <- strsplit(x, "\\$")
obj <- unique(sapply(sx, `[`, 1))
stopifnot(length(obj) == 1L)
obj <- get(obj, ...)
obj[sapply(sx, `[`, 2)]
}

col_names <- c("iris$Sepal.Length", "iris$Sepal.Width")
head(get3(col_names))

> head(get3(col_names))
Sepal.Length Sepal.Width
1 5.1 3.5
2 4.9 3.0
3 4.7 3.2
4 4.6 3.1
5 5.0 3.6
6 5.4 3.9

If you have multiple objects referenced in col_names then you will need a different solution, along the lines of

get4 <- function(x, ...) {
fun <- function(text, ...) {
obj <- get(text[1], ...)
obj[[text[2]]]
}
sx <- strsplit(x, "\\$")
lx <- lapply(sx, fun, ...)
out <- do.call(cbind.data.frame, lx)
names(out) <- x
out
}

col_names2 <- c("iris$Sepal.Length", "iris2$Sepal.Length")
get4(col_names2)

> head(get4(col_names2))
iris$Sepal.Length iris2$Sepal.Length
1 5.1 5.1
2 4.9 4.9
3 4.7 4.7
4 4.6 4.6
5 5.0 5.0
6 5.4 5.4

How to pass string as column name in R

We can use get

col= paste0(m,"_var1")
get(r)[col]

# jan_var1
#1 1
#2 2
#3 3
#4 4
#5 5

get(r) would return the entire dataframe having name as jan5e_results and from that we select col column to subset.

#  jan_var1  b
#1 1 6
#2 2 7
#3 3 8
#4 4 9
#5 5 10

data

jan5e_results <- data.frame(jan_var1 = 1:5, b = 6:10)

Passing string as column name in lag()

You may use !! instead of {{}}

df %>%
mutate(!!new_col_name := lag(!!as.symbol(new_col_value)))

age value_2010 value_lag
1 1 10 NA
2 2 20 10
3 3 30 20

error in pass string as column name R t test

If you want to use srings in formulas you can use reformulate.

Your example data has too few observations to work with a double grouped pairwise_t_test, but the general notation would be as follows:

library(dplyr)
library(rstatix)

outcome_var <- "Sepal.Length"
grouping_var <- "Species"

iris %>%
pairwise_t_test(., reformulate(grouping_var, outcome_var), paired = FALSE, p.adjust.method = "holm")
#> # A tibble: 3 x 9
#> .y. group1 group2 n1 n2 p p.signif p.adj p.adj.signif
#> * <chr> <chr> <chr> <int> <int> <dbl> <chr> <dbl> <chr>
#> 1 Sepal.Le… setosa versic… 50 50 8.77e-16 **** 1.75e-15 ****
#> 2 Sepal.Le… setosa virgin… 50 50 2.21e-32 **** 6.64e-32 ****
#> 3 Sepal.Le… versico… virgin… 50 50 2.77e- 9 **** 2.77e- 9 ****

Created on 2020-07-08 by the reprex package (v0.3.0)

Is it possible to pass a string of column names to tidy-select?

You can use all_of:

pivot_longer(df, cols = all_of(column),...)

From the tidyr manual:

If you have a character vector of column names, use all_of() or
any_of(), depending on whether or not you want unknown variable names
to cause an error, e.g unnest(df, all_of(vars)), unnest(df,
-any_of(vars))
.

Pass a string as variable name in dplyr::filter

!! or UQ evaluates the variable, so mtcars %>% filter(!!var == 4) is the same as mtcars %>% filter('cyl' == 4) where the condition always evaluates to false; You can prove this by printing !!var in the filter function:

mtcars %>% filter({ print(!!var); (!!var) == 4 })
# [1] "cyl"
# [1] mpg cyl disp hp drat wt qsec vs am gear carb
# <0 rows> (or 0-length row.names)

To evaluate var to the cyl column, you need to convert var to a symbol of cyl first, then evaluate the symbol cyl to a column:

Using rlang:

library(rlang)
var <- 'cyl'
mtcars %>% filter((!!sym(var)) == 4)

# mpg cyl disp hp drat wt qsec vs am gear carb
#1 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
#2 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
#3 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
# ...

Or use as.symbol/as.name from baseR:

mtcars %>% filter((!!as.symbol(var)) == 4)

mtcars %>% filter((!!as.name(var)) == 4)

Can I pass a column name to a substring function using a variable?

No, you cannot do it that way. SQL normally has to know what columns you are working with for optimization. One way to do it would be to Have each column name in the where clause:

DELETE TOP (@Dimension) 
FROM [dbo].[TestDelete]
WHERE (@ColumnName = 'DateReceived' and SUBSTRING(DateReceived, 1, 2) = @Year)
OR (@ColumnName = 'DateSent' and SUBSTRING(DateSent, 1, 2) = @Year)

Another way would be to use sp_executesql and create the delete as a string:

declare @sql nvarchar(max) = concat('DELETE TOP(@Dimension) ',
'FROM [dbo].[TestDelete] ',
'WHERE SUBSTRING(', @ColumnName, ', 1, 2) = @Year');
exec sp_executesql @sql,
N'@Dimension int', N'Year int',
@Dimension = @Dimension, @Year = @Year;

I don't think either is a good idea though. Why do you need the column name to be passed?



Related Topics



Leave a reply



Submit