表达式评估,来自字符串的对象......仍然不清楚

时间:2017-02-20 14:31:32

标签: r expression evaluation tidyverse

我已经阅读了许多关于如何使用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个矩阵,分别具有dfAdfBdfC值。 矩阵名称也将具有扩展值的名称。 行名称将是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编码规则......)

非常感谢您的回答,对此长篇文章感到抱歉!

3 个答案:

答案 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表格。

对于dfAdfBdfC以及两个新列中的每个单元格,长格式将有一行:

  • 新的"vnames"列包含原始"dfA""dfB""dfC"列中的数值。 v.names的{​​{1}}和varying参数定义了这一点。
  • reshape列标识"Group"列中每个值df的哪一列来自。 "vnames"timevar参数定义了这一点。

这种方法的优点是可以轻松查看所有切片。例如,尝试以下各项: timestab["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.tablevalue.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