R 为什么从数据帧和TIBLE中子集一列会得到不同的结果

R 为什么从数据帧和TIBLE中子集一列会得到不同的结果,r,dataframe,dplyr,subset,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.bobbibro

这是一个“为什么”的问题,而不是一个“如何”的问题

我有一个
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与dataframe的
str
看起来相同,但输出不同。我想知道为什么

如果将
的结果打印为.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)"
库(TIBLE)

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

  • 默认情况下,
    [.data.frame
    将在结果只有一列时删除维度,类似于矩阵子集的工作方式。因此结果是向量
  • [.tbl_df
    永远不会像这样删除维度;它总是返回一个tbl
反过来,
as.character
忽略tbl的类,将其视为一个普通列表。而在列表上调用的
as.character
的行为类似于
deparse
:它返回的字符表示是R代码,可以解析并执行该代码来重现列表

在大多数情况下,tbl行为可以说是正确的,因为删除维度很容易导致错误:子集一个数据帧通常会导致另一个数据帧,但有时不会。在这种特定情况下,它不会做您想要的事情


如果您想从tbl中提取一列作为向量,可以使用列表式索引:
URL[[4]]
URL$final\u domain

我认为您的问题的基本答案是Hadley Wickham在编写tibble 1.0时希望
的行为一致[
运算符。这一决定在Wickham的《高级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]
对于TIBLE(或dplyr),默认情况下我们的行为是一致的,因此无论返回多少列,当使用
[
操作符进行子集设置时,我们都可以确保拥有相同的对象类:

library(tibble)
data_df <- tibble(a = runif(10), b = letters[1:10])

data_df[, 2]
data_df[, 1:2]
库(TIBLE)

数据_df可能是因为
tbl _df
data.frame
具有不同的索引属性。当从
data.frame
索引一列时,您会得到一个
向量
,但当您从
tbl _df
索引一列时,您仍然会得到一个
tbl _df
。此外,
length
还会返回列数当给定一个
data.frame
和一个
vector
时的行数。也许可以添加
dplyr::pull
作为一种将列提取为向量的方法(在管道中时不时使用)。
library(tibble)
data_df <- tibble(a = runif(10), b = letters[1:10])

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