表达式评估,字符串中的对象......仍然不清楚
Expression evaluation, objects from string... still not clear
我阅读了很多关于如何使用 assign()
、get()
、as.name()
或 eval(substitute())
从字符串创建对象或计算表达式的主题,但有些东西我不是很清楚。
在我下面的示例中,我有一个输入数据集(长格式),我想要输出 3 个矩阵。
输入数据帧:
# Input dataframe
df <- data.frame(v1 = c(rep("A", 3), rep("B", 3)),
v2 = c(rep(letters[1:3], 2)),
dfA = sample(1:10, 6),
dfB = sample(1:10, 6),
dfC = sample(1:10, 6))
> df
v1 v2 dfA dfB dfC
1 A a 5 10 5
2 A b 4 7 9
3 A c 10 1 2
4 B a 7 9 7
5 B b 2 8 1
6 B c 9 3 4
3个变量的前缀是数据集名称(然后是"A"、"B"、"C")。
当以 v2
作为键传播时,我想要 3 个分别具有 dfA
、dfB
和 dfC
值的矩阵。
矩阵名称也将具有传播值的名称。
行名称将是 v1
值。
我想要的矩阵:
> dfA
a b c
A 4 1 2
B 5 8 7
> dfB
a b c
A 1 9 5
B 10 4 2
> dfC
a b c
A 5 1 3
B 6 4 9
所以,手动,没问题(我只是在这里做第一个矩阵):
library(dplyr)
library(tidyr)
dfA <- df %>% select(v1, v2, dfA) %>% spread(key = v2, value = dfA)
row.names(dfA) <- dfA[, 1]
dfA <- dfA %>% select(-1) %>% as.matrix()
> dfA
a b c
A 4 1 2
B 5 8 7
但是现在,我有很多输入数据框,所以我想自动完成。
我将做一个以 DF 名称作为参数的函数,但现在我想一步步向您展示没有函数。
dfName = "df"
# For : dfA <- df %>% select(v1, v2, dfA) :
assign(paste0(dfName, "A"), get(dfName) %>% select(v1, v2, get(paste0(dfName, "A"))))
> dfA
v1 v2 dfA
1 A a 5
2 A b 4
3 A c 10
4 B a 7
5 B b 2
6 B c 9
# For : dfA <- dfA %>% spread(key = v2, value = dfA)
assign(paste0(dfName, "A"), get(paste0(dfName, "A")) %>%
spread(key = v2, value = get(paste0(dfName, "A"))))
Error: Invalid column specification
那里,我的传播函数有错误。
它在上面的 select()
中起作用,但在 spread() 中不起作用...我应该怎么做?
好吧,我唯一能做到的方法是:
eval(substitute(var1 <- var1 %>% spread(key = v2, value = var1),
list(var1 = as.name(paste0(dfName, "A")))))
> dfA
v1 a b c
1 A 5 4 10
2 B 7 2 9
然后,我想将 v1 值放入 row.names :
row.names(get(paste0(dfName, "A"))) <- get(paste0(dfName, "A"))[, 1]
...错误(我不明白):
Error in ... could not find function "get<-"
但我设法做到了:
eval(substitute(row.names(var1) <- var1[, 1],
list(var1 = as.name(paste0(dfName, "A")))))
> dfA
v1 a b c
A A 5 4 10
B B 7 2 9
最后把它变成矩阵:
assign(paste0(dfName, "A"), get(paste0(dfName, "A")) %>% select(-1) %>% as.matrix())
> dfA
a b c
A 5 4 10
B 7 2 9
所以,我可以让它工作,但它对我来说不是 "pretty"...
是 assign()
太多还是 eval(substitute())
太多?
这是做这件事的好方法吗?
我想我几乎把手指放在上面了,但我仍然不明白我可能有的错误。
我错过了什么吗?有更好的方法吗? (更好,我的意思是编码清晰,效率更高,遵守 R 编码规则...)
非常感谢您的回答,很抱歉这么长post!
1) Map 通常最好创建一个对象列表,所以使用最后注释中的输入并利用 xtabs
试试这个。
没有使用包。
fun <- function(nm) xtabs(df[c(nm, head(names(df), 2))])
L <- Map(fun, tail(names(df), -2))
names(L) <- sub("df", "", names(L)) # optional - remove "df" from names
给予(输出后续):
> L
$A
v2
v1 a b c
A 3 8 4
B 7 6 1
$B
v2
v1 a b c
A 6 9 5
B 4 10 3
$C
v2
v1 a b c
A 7 6 1
B 10 2 8
2) reshape 另一种可能性是通过将 df
重塑为长格式然后在其上使用 xtabs
来创建 3d table .
dfA
、dfB
和 dfC
中的每个单元格在长表格中各占一行,还有两个新列:
- 新的
"vnames"
列包含原始 "dfA"
、"dfB"
、"dfC"
列中的数值。 v.names
和 varying
参数 reshape
定义了这一点。
- 新的
"Group"
列标识 "vnames"
列中的每个值来自 df
的哪一列。 timevar
和 times
参数定义了这一点。
这种方法的优点是可以轻松查看所有切片。例如,尝试以下各项:
tab["B", "b", "C"]
、tab[,"b", "C"]
、tab["B", "b", ]
、tab["B",,"C"]
、tab["B",,]
、tab[,"b",]
、tab[,,"C"]
没有使用包。
vnames <- tail(names(df), -2) # c("dfA", "dfB", "dfC")
long <- reshape(df, dir = "long",
varying = vnames, v.names = "vnames",
times = sub("df", "", vnames), timevar = "Group")
tab <- xtabs(vnames ~ v1 + v2 + Group, long)
给予:
> tab
, , Group = A
v2
v1 a b c
A 3 8 4
B 7 6 1
, , Group = B
v2
v1 a b c
A 6 9 5
B 4 10 3
, , Group = C
v2
v1 a b c
A 7 6 1
B 10 2 8
2a) reshape2::melt 此变体使用 reshape2 包中的 melt
代替 R 基础中的 reshape
并且实质上是更紧凑:
library(reshape2)
xtabs(value ~ v1 + v2 + variable, data = melt(df))
注意: 为可重复性设置种子,我们将此输入用作 df
:
set.seed(123)
df <- data.frame(v1 = c(rep("A", 3), rep("B", 3)),
v2 = c(rep(letters[1:3], 2)),
dfA = sample(1:10, 6),
dfB = sample(1:10, 6),
dfC = sample(1:10, 6))
您需要这些函数的 standard evaluation
版本:
assign(paste0(dfName, "A"), get(paste0(dfName, "A")) %>%
spread_(key = 'v2', value = paste0(dfName, "A")))
注意动词末尾的 _
,以及 "v2"
被引用的事实。
来自 dplyr
插图:
Standard evaluation basics
Every function in dplyr that uses NSE also has a version that uses SE.
The name of the SE version is always the NSE name with an _ on the
end. For example, the SE version of summarise() is summarise_(); the
SE version of arrange() is arrange_(). These functions work very
similarly to their NSE cousins, but their inputs must be “quoted”:
我们可以在不使用 dcast
从 data.table
转换为 list
的情况下执行此操作,它可以将多个列作为 value.var
library(data.table)
dcast(setDT(df), v1 ~v2, value.var = c("dfA", "dfB", "dfC"))
# v1 dfA_a dfA_b dfA_c dfB_a dfB_b dfB_c dfC_a dfC_b dfC_c
#1: A 3 8 4 6 9 5 7 6 1
#2: B 7 6 1 4 10 3 10 2 8
数据
set.seed(123)
df <- data.frame(v1 = c(rep("A", 3), rep("B", 3)),
v2 = c(rep(letters[1:3], 2)),
dfA = sample(1:10, 6),
dfB = sample(1:10, 6),
dfC = sample(1:10, 6))
我阅读了很多关于如何使用 assign()
、get()
、as.name()
或 eval(substitute())
从字符串创建对象或计算表达式的主题,但有些东西我不是很清楚。
在我下面的示例中,我有一个输入数据集(长格式),我想要输出 3 个矩阵。
输入数据帧:
# Input dataframe
df <- data.frame(v1 = c(rep("A", 3), rep("B", 3)),
v2 = c(rep(letters[1:3], 2)),
dfA = sample(1:10, 6),
dfB = sample(1:10, 6),
dfC = sample(1:10, 6))
> df
v1 v2 dfA dfB dfC
1 A a 5 10 5
2 A b 4 7 9
3 A c 10 1 2
4 B a 7 9 7
5 B b 2 8 1
6 B c 9 3 4
3个变量的前缀是数据集名称(然后是"A"、"B"、"C")。
当以 v2
作为键传播时,我想要 3 个分别具有 dfA
、dfB
和 dfC
值的矩阵。
矩阵名称也将具有传播值的名称。
行名称将是 v1
值。
我想要的矩阵:
> dfA
a b c
A 4 1 2
B 5 8 7
> dfB
a b c
A 1 9 5
B 10 4 2
> dfC
a b c
A 5 1 3
B 6 4 9
所以,手动,没问题(我只是在这里做第一个矩阵):
library(dplyr)
library(tidyr)
dfA <- df %>% select(v1, v2, dfA) %>% spread(key = v2, value = dfA)
row.names(dfA) <- dfA[, 1]
dfA <- dfA %>% select(-1) %>% as.matrix()
> dfA
a b c
A 4 1 2
B 5 8 7
但是现在,我有很多输入数据框,所以我想自动完成。 我将做一个以 DF 名称作为参数的函数,但现在我想一步步向您展示没有函数。
dfName = "df"
# For : dfA <- df %>% select(v1, v2, dfA) :
assign(paste0(dfName, "A"), get(dfName) %>% select(v1, v2, get(paste0(dfName, "A"))))
> dfA
v1 v2 dfA
1 A a 5
2 A b 4
3 A c 10
4 B a 7
5 B b 2
6 B c 9
# For : dfA <- dfA %>% spread(key = v2, value = dfA)
assign(paste0(dfName, "A"), get(paste0(dfName, "A")) %>%
spread(key = v2, value = get(paste0(dfName, "A"))))
Error: Invalid column specification
那里,我的传播函数有错误。
它在上面的 select()
中起作用,但在 spread() 中不起作用...我应该怎么做?
好吧,我唯一能做到的方法是:
eval(substitute(var1 <- var1 %>% spread(key = v2, value = var1),
list(var1 = as.name(paste0(dfName, "A")))))
> dfA
v1 a b c
1 A 5 4 10
2 B 7 2 9
然后,我想将 v1 值放入 row.names :
row.names(get(paste0(dfName, "A"))) <- get(paste0(dfName, "A"))[, 1]
...错误(我不明白):
Error in ... could not find function "get<-"
但我设法做到了:
eval(substitute(row.names(var1) <- var1[, 1],
list(var1 = as.name(paste0(dfName, "A")))))
> dfA
v1 a b c
A A 5 4 10
B B 7 2 9
最后把它变成矩阵:
assign(paste0(dfName, "A"), get(paste0(dfName, "A")) %>% select(-1) %>% as.matrix())
> dfA
a b c
A 5 4 10
B 7 2 9
所以,我可以让它工作,但它对我来说不是 "pretty"...
是 assign()
太多还是 eval(substitute())
太多?
这是做这件事的好方法吗?
我想我几乎把手指放在上面了,但我仍然不明白我可能有的错误。
我错过了什么吗?有更好的方法吗? (更好,我的意思是编码清晰,效率更高,遵守 R 编码规则...)
非常感谢您的回答,很抱歉这么长post!
1) Map 通常最好创建一个对象列表,所以使用最后注释中的输入并利用 xtabs
试试这个。
没有使用包。
fun <- function(nm) xtabs(df[c(nm, head(names(df), 2))])
L <- Map(fun, tail(names(df), -2))
names(L) <- sub("df", "", names(L)) # optional - remove "df" from names
给予(输出后续):
> L
$A
v2
v1 a b c
A 3 8 4
B 7 6 1
$B
v2
v1 a b c
A 6 9 5
B 4 10 3
$C
v2
v1 a b c
A 7 6 1
B 10 2 8
2) reshape 另一种可能性是通过将 df
重塑为长格式然后在其上使用 xtabs
来创建 3d table .
dfA
、dfB
和 dfC
中的每个单元格在长表格中各占一行,还有两个新列:
- 新的
"vnames"
列包含原始"dfA"
、"dfB"
、"dfC"
列中的数值。v.names
和varying
参数reshape
定义了这一点。 - 新的
"Group"
列标识"vnames"
列中的每个值来自df
的哪一列。timevar
和times
参数定义了这一点。
这种方法的优点是可以轻松查看所有切片。例如,尝试以下各项:
tab["B", "b", "C"]
、tab[,"b", "C"]
、tab["B", "b", ]
、tab["B",,"C"]
、tab["B",,]
、tab[,"b",]
、tab[,,"C"]
没有使用包。
vnames <- tail(names(df), -2) # c("dfA", "dfB", "dfC")
long <- reshape(df, dir = "long",
varying = vnames, v.names = "vnames",
times = sub("df", "", vnames), timevar = "Group")
tab <- xtabs(vnames ~ v1 + v2 + Group, long)
给予:
> tab
, , Group = A
v2
v1 a b c
A 3 8 4
B 7 6 1
, , Group = B
v2
v1 a b c
A 6 9 5
B 4 10 3
, , Group = C
v2
v1 a b c
A 7 6 1
B 10 2 8
2a) reshape2::melt 此变体使用 reshape2 包中的 melt
代替 R 基础中的 reshape
并且实质上是更紧凑:
library(reshape2)
xtabs(value ~ v1 + v2 + variable, data = melt(df))
注意: 为可重复性设置种子,我们将此输入用作 df
:
set.seed(123)
df <- data.frame(v1 = c(rep("A", 3), rep("B", 3)),
v2 = c(rep(letters[1:3], 2)),
dfA = sample(1:10, 6),
dfB = sample(1:10, 6),
dfC = sample(1:10, 6))
您需要这些函数的 standard evaluation
版本:
assign(paste0(dfName, "A"), get(paste0(dfName, "A")) %>%
spread_(key = 'v2', value = paste0(dfName, "A")))
注意动词末尾的 _
,以及 "v2"
被引用的事实。
来自 dplyr
插图:
Standard evaluation basics
Every function in dplyr that uses NSE also has a version that uses SE. The name of the SE version is always the NSE name with an _ on the end. For example, the SE version of summarise() is summarise_(); the SE version of arrange() is arrange_(). These functions work very similarly to their NSE cousins, but their inputs must be “quoted”:
我们可以在不使用 dcast
从 data.table
转换为 list
的情况下执行此操作,它可以将多个列作为 value.var
library(data.table)
dcast(setDT(df), v1 ~v2, value.var = c("dfA", "dfB", "dfC"))
# v1 dfA_a dfA_b dfA_c dfB_a dfB_b dfB_c dfC_a dfC_b dfC_c
#1: A 3 8 4 6 9 5 7 6 1
#2: B 7 6 1 4 10 3 10 2 8
数据
set.seed(123)
df <- data.frame(v1 = c(rep("A", 3), rep("B", 3)),
v2 = c(rep(letters[1:3], 2)),
dfA = sample(1:10, 6),
dfB = sample(1:10, 6),
dfC = sample(1:10, 6))