1

我已经阅读了许多关于如何从字符串创建对象或使用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,dfBdfC值。矩阵名称也将具有扩展值的名称。行名将是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编码规则......)

非常感谢您的回答,很抱歉这篇长文!

4

3 回答 3

3

1) Map通常最好创建一个对象列表,所以在最后使用 Note 中的输入并使用xtabstry this。

没有使用任何包。

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为长表格然后使用它来创建一个3d表格xtabs

长格式中的每个单元格都有一行dfAdfB以及dfC两个新列:

  • 新列包含原始, ,列"vnames"中的数值。和参数定义了这一点。"dfA""dfB""dfC"v.namesvaryingreshape
  • "Group"列标识列中的df每个值"vnames"来自哪一列。timevartimes参数定义了这一点。

这种方法的优点是可以轻松查看所有切片。例如,尝试以下每个: 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这个变体使用meltreshape2 包代替reshapeR 的基础,并且更加紧凑:

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))
于 2017-02-20T14:47:34.863 回答
1

您需要standard evaluation这些功能的版本:

assign(paste0(dfName, "A"), get(paste0(dfName, "A")) %>%
           spread_(key = 'v2', value = paste0(dfName, "A")))

注意_动词末尾的 the 和"v2"引用的事实。

从小dplyr插曲:

标准评估基础

dplyr 中使用 NSE 的每个函数也有一个使用 SE 的版本。SE 版本的名称始终是 NSE 名称,末尾带有 _。例如,summarise() 的 SE 版本是 summarise_(​​);安排()的 SE 版本是安排_()。这些函数的工作方式与它们的 NSE 表亲非常相似,但它们的输入必须被“引用”:

于 2017-02-20T14:49:09.430 回答
0

我们可以做到这一点,而无需转换为listwith dcastfrom data.tablewhich can take multiple columns asvalue.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))
于 2017-02-20T14:58:59.987 回答