我已经阅读了许多关于如何使用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
所以,我可以让它发挥作用,但对我来说并不“很漂亮”......
是assign()
太多还是eval(substitute())
太多了?
这是做这件事的好方法吗?
我想我几乎把手指放在上面,但我仍然不明白我可以犯的错误。
我错过了什么吗?有没有更好的方法呢? (更好的是,我的意思是清晰的编码,更高的效率,尊重R编码规则......)
非常感谢您的回答,对此长篇文章感到抱歉!
答案 0 :(得分:3)
1)地图通常最好创建一个对象列表,这样最后使用Note中的输入并使用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)重塑另一种可能性是通过将df
重塑为长格式然后在其上使用xtabs
来创建一个3d表格。
对于dfA
,dfB
和dfC
以及两个新列中的每个单元格,长格式将有一行:
"vnames"
列包含原始"dfA"
,"dfB"
,"dfC"
列中的数值。 v.names
的{{1}}和varying
参数定义了这一点。reshape
列标识"Group"
列中每个值df
的哪一列来自。 "vnames"
和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)
2a)reshape2 :: melt 此变体使用reshape2包中的> 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
代替R基础中的melt
,并且更加紧凑:
reshape
注意:设置种子以获得可重复性我们将此输入用作library(reshape2)
xtabs(value ~ v1 + v2 + variable, data = melt(df))
:
df
答案 1 :(得分:1)
您需要这些功能的standard evaluation
版本:
assign(paste0(dfName, "A"), get(paste0(dfName, "A")) %>%
spread_(key = 'v2', value = paste0(dfName, "A")))
请注意动词末尾的_
以及引用"v2"
的事实。
来自dplyr
小插曲:
标准评估基础
使用NSE的dplyr中的每个函数也都有一个使用SE的版本。 SE版本的名称始终是带有_的NSE名称 结束。例如,summary()的SE版本是summarise_();该 Arrange()的SE版本是arrange_()。这些功能非常有效 类似于他们的NSE表兄弟,但他们的输入必须“引用”:
答案 2 :(得分:0)
我们可以执行此操作而无需转换为list
dcast
来自data.table
,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))
config.vm.network "forwarded_port", guest: 5432, host: 5432