为什么从数据帧和tibble对列进行子集化会产生不同的结果

时间:2016-10-07 13:44:18

标签: r dataframe dplyr subset

这是一个'为什么'的问题而不是'如何'问题。

由于汇总tibble

,我有一个dplyr
> str(urls)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   144 obs. of  4 variables:
 $ BRAND       : chr  "Bobbi Brown" "Calvin Klein" "Chanel" "Clarins" ...
 $ WEBSITE     : chr  "http://www.bobbibrowncosmetics.com/" "http://www.calvinklein.com/shop/en/ck" "http://www.chanel.com/en_US/" "http://www.clarinsusa.com/" ...
 $ domain      : chr  "bobbibrowncosmetics.com/" "calvinklein.com/shop/en/ck" "chanel.com/en_US/" "clarinsusa.com/" ...
 $ final_domain: chr  "bobbibrowncosmetics.com/" "calvinklein.com/shop/en/ck" "chanel.com/en_US/" "clarinsusa.com/" ...

当我尝试将final_domain列提取为字符向量时,会发生以下情况:

> length(as.character(urls[ ,4]))
[1] 1

当我改为强制数据框然后执行时,我得到了我真正想要的东西:

> length(as.character(as.data.frame(urls)[ ,4]))
[1] 144

tibble与数据帧的str看起来相同但输出不同。我想知道为什么?

3 个答案:

答案 0 :(得分:13)

根本原因是,当只选择一列时,对tbl和数据帧进行子集会产生不同的结果。

  • 默认情况下,如果结果只有1列,[.data.frame将删除维度,类似于矩阵子集的工作方式。所以结果是一个矢量。
  • [.tbl_df 永远不会删除这样的尺寸;它总是会返回一个tbl。

反过来,as.character忽略tbl的类,将其视为普通列表。 as.character调用列表的行为类似于deparse:它返回的字符表示形式是可以解析并执行以重现列表的R代码。

在大多数情况下,tbl行为可以说是正确的做法,因为丢弃维度很容易导致错误:对数据框进行子集化通常会导致另一个数据框,但有时它并不会。在这种特殊情况下,它没有做你想做的事。

如果要从tbl中提取列作为向量,可以使用列表样式索引:urls[[4]]urls$final_domain

答案 1 :(得分:3)

我认为你问题的基本答案是Hadley Wickham在编写 tibble 1.0时,希望[运算符的行为一致。在[{3}}中的Wickham的 Advanced R 中间接地讨论了这个决定:

  

理解简化和简化之​​间的区别非常重要   保留子集。简化子集返回最简单的   可以表示输出的可能数据结构,并且很有用   交互式,因为它通常会给你你想要的东西。保   子集化使输出结构与输入相同,   并且通常更适合编程,因为结果总是如此   是同一类型。当子集化矩阵时,省略drop = FALSE   数据帧是最常见的编程错误源之一。   (它适用于您的测试用例,但有人会传入   单列数据框,它将在意外和不清楚的情况下失败   方式。)

在这里,我们可以清楚地看到Hadley关注[.data.frame的不一致默认行为,以及为什么他会选择更改 tibble 中的行为。

考虑到上述术语,很容易看出[.data.frame运算符默认情况下是生成简化子集还是保留子集取决于输入而不是编程。例如,采用数据框data_df并对其进行子集化:

data_df <- data.frame(a = runif(10), b = letters[1:10])

data_df[, 2]
data_df[, 1:2]

在一个案例中得到一个向量,在另一个案例中得到一个数据框。要预测输出的类型,您必须事先知道将要对哪些列进行子集化(即,您必须知道length(list_of_columns)),这可能来自用户输入,或者您需要显式添加{ {1}}参数。因此,以下内容生成相同的对象类,但在第二种情况下添加的参数是不必要的(大多数R用户可能不知道):

drop =

使用 tibble (或 dplyr ),默认情况下我们有一致的行为,因此我们可以确保在使用{{1进行子集化时具有相同类别的对象运算符无论我们返回多少列:

data_df[, 2, drop = FALSE]
data_df[, 1:2, drop = FALSE]

答案 2 :(得分:1)

如果您打印as.character的结果,您会发现差异:

library(tibble)
x <- tribble(
    ~x, ~y,  ~z,
    "a", 2,  3.6,
    "b", 1,  8.5
)

as.character(as.data.frame(x)[ ,2])
# [1] "2" "1"

as.character(x[ ,2])
# "c(2, 1)"

as.character将列转换为单个字符串。这个帖子应该有用:https://stackoverflow.com/questions/21618423/extract-a-dplyr-tbl-column-as-a-vector