Extraer una columna dplyr tbl como vector


175

¿Hay una manera más sucinta de obtener una columna de un dplyr tbl como vector, a partir de un tbl con el back-end de la base de datos (es decir, el marco / tabla de datos no puede ser subconjunto directamente)?

require(dplyr)
db <- src_sqlite(tempfile(), create = TRUE)
iris2 <- copy_to(db, iris)
iris2$Species
# NULL

Eso hubiera sido demasiado fácil, así que

collect(select(iris2, Species))[, 1]
# [1] "setosa"     "setosa"     "setosa"     "setosa"  etc.

Pero parece un poco torpe.


es collect(iris2)$Speciesmenos torpe?
CJ Yetman

Respuestas:


178

Con dplyr 0.7.0, puede usar pullpara obtener un vector de a tbl.


library("dplyr")
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
db <- src_sqlite(tempfile(), create = TRUE)
iris2 <- copy_to(db, iris)
vec <- pull(iris2, Species)
head(vec)
#> [1] "setosa" "setosa" "setosa" "setosa" "setosa" "setosa"

96

Según el comentario de @nacnudus, parece que pullse implementó una función en dplyr 0.6:

iris2 %>% pull(Species)

Para versiones anteriores de dplyr, aquí hay una función ordenada para hacer que extraer una columna sea un poco más agradable (más fácil de escribir y más fácil de leer):

pull <- function(x,y) {x[,if(is.name(substitute(y))) deparse(substitute(y)) else y, drop = FALSE][[1]]}

Esto le permite hacer cualquiera de estos:

iris2 %>% pull('Species')
iris2 %>% pull(Species)
iris2 %>% pull(5)

Resultando en...

 [1] 21.0 21.0 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 17.8 16.4 17.3 15.2 10.4 10.4 14.7 32.4 30.4 33.9 21.5 15.5 15.2 13.3 19.2 27.3 26.0 30.4 15.8 19.7 15.0 21.4

Y también funciona bien con marcos de datos:

> mtcars %>% pull(5)
 [1] 3.90 3.90 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 3.92 3.07 3.07 3.07 2.93 3.00 3.23 4.08 4.93 4.22 3.70 2.76 3.15 3.73 3.08 4.08 4.43
[28] 3.77 4.22 3.62 3.54 4.11

Una buena manera de hacer esto en v0.2 de dplyr:

iris2 %>% select(Species) %>% collect %>% .[[5]]

O si lo prefieres:

iris2 %>% select(Species) %>% collect %>% .[["Species"]]

O si tu mesa no es demasiado grande, simplemente ...

iris2 %>% collect %>% .[["Species"]]

2
Me gusta tu función pull. Solo agregaría una simplificación para los casos en los que solo hay una variable: pull <- function(x, y) { if (ncol(x) == 1) y <- 1 else y x[ , if (is.name(substitute(y))) deparse(substitute(y)) else y, drop = FALSE][[1]] }para que pueda continuariris2 %>% pull()
Rappster

77
También puede usar el magrittroperador de exposición ( %$%) para extraer un vector de un marco de datos. es decir iris2 %>% select(Species) %>% collect() %$% Species.
marino el

@ Luke1018 debería crear una respuesta a partir de este comentario
rrs

pull()se implementará en dplyr versión 0.6 github.com/tidyverse/dplyr/commit/…
nacnudus

72

También puede usar lo unlistque me parece más fácil de leer porque no necesita repetir el nombre de la columna o especificar el índice.

iris2 %>% select(Species) %>% unlist(use.names = FALSE)

1
Este parece ser el método más versátil, ya que funciona de manera idéntica con vectores y data.frames, es decir, permite que las funciones sean más agnósticas.
geotheory

Estaba buscando una respuesta a esta pregunta exacta y unlistes precisamente lo que necesitaba. ¡Gracias!
Andrew Brēza

unlistTambién puede extraer valores de múltiples columnas (combinando todos los valores en un solo vector), mientras que dplyr::pullestá limitado a una sola columna.
filups21

21

Usaría la extract2función de conveniencia de magrittr:

library(magrittr)
library(dplyr)

iris2 %>%
  select(Species) %>%
  extract2(1)  

¿Querías usar collect()entre selecty extract2?
nacnudus

10
use_series(Species)es quizás aún más legible. Gracias por alertarme sobre estas funciones, hay varias otras útiles de donde provienen.
nacnudus

20

Probablemente escribiría:

collect(select(iris2, Species))[[1]]

Dado que dplyr está diseñado para trabajar con tbls de datos, no hay mejor manera de obtener una sola columna de datos.


No puedo decir más justo que eso. Surgió interactivamente en la consola cuando intenté usar unico (tabla $ columna) para verificar valores espurios.
nacnudus

44
@nacnudus para ese caso también podrías hacerlogroup_by(column) %.% tally()
hadley

12
Un argumento drop = TRUEpara dplyr::selectsería sorprendente para los muchos casos de uso en los que realmente necesitamos extraer los vectores.
Antoine Lizée

Esta fue la única forma en que pude sacar una columna de mi Sparklyr sdf. Pull no funcionaba para mí en la versión 0.7.8.
Meep

16

@ Luke1018 propuso esta solución en uno de los comentarios:

También puede usar el magrittroperador de exposición ( %$%) para extraer un vector de un marco de datos.

Por ejemplo:

iris2 %>% select(Species) %>% collect() %$% Species

Pensé que merecía su propia respuesta.


Yo estaba buscando esto.
Diego-MX

¿Cómo haría esto si no quiero pasar el colname en sí, sino una variable de cadena que lo contiene?
mzuba

@mzuba tibble(x = 1:10, y = letters[1:10]) %>% select_("x") %>% unlist()y también podría agregar otro %>% unname()al final si lo desea, pero para mis propósitos no he encontrado que el último eslabón de la cadena de tuberías sea necesario. También puede especificar use.names = FALSEen el unlist()comando, que hace lo mismo que también se agrega unname()a la cadena de tuberías.
Mark White

1
@mzuba Usaría el pullcomando ahora. Mi solución fue escrita antes de la dplyrversión 0.6.
rrs

1
Tenga en cuenta que %$%funciona en cualquier lista, mientras pull()que no
wint3rschlaefer

2

Si está acostumbrado a usar corchetes para indexar, otra opción es simplemente ajustar el enfoque de indexación habitual en una llamada a deframe () , por ejemplo:

library(tidyverse)

iris2 <- as_tibble(iris)

# using column name
deframe(iris2[, 'Sepal.Length'])

# [1] 5.1 4.9 4.7 4.6 5.0 5.4

# using column number
deframe(iris2[, 1])

# [1] 5.1 4.9 4.7 4.6 5.0 5.4

Eso y pull () son dos formas bastante buenas de obtener una columna tibble.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.