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.

nacnudus
fuente
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"
Lorenz Walthert
fuente
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"]]
Tommy O'Dell
fuente
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)
StanislawSwierc
fuente
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)  
Hugh
fuente
¿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.

Hadley
fuente
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.

rrs
fuente
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.

Keith Hughitt
fuente