Extraire une colonne tbl dplyr en tant que vecteur

175

Existe-t-il un moyen plus succinct d'obtenir une colonne d'un tbl dplyr en tant que vecteur, à partir d'un tbl avec une base de données principale (c'est-à-dire que la trame / table de données ne peut pas être directement sous-ensemble)?

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

Cela aurait été trop facile, alors

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

Mais cela semble un peu maladroit.

nacnudus
la source
est collect(iris2)$Speciesmoins maladroit?
CJ Yetman

Réponses:

178

Avec dplyr 0.7.0, vous pouvez utiliser pullpour obtenir un vecteur à partir d'un fichier 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
la source
96

Selon le commentaire de @nacnudus, il semble qu'une pullfonction a été implémentée dans dplyr 0.6:

iris2 %>% pull(Species)

Pour les anciennes versions de dplyr, voici une fonction intéressante pour rendre l'extraction d'une colonne un peu plus agréable (plus facile à taper et plus facile à lire):

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

Cela vous permet de faire l'une des choses suivantes:

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

Résultant 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

Et cela fonctionne également très bien avec les trames de données:

> 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

Une bonne façon de faire cela dans la v0.2 de dplyr:

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

Ou si vous préférez:

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

Ou si votre table n'est pas trop grande, tout simplement ...

iris2 %>% collect %>% .[["Species"]]
Tommy O'Dell
la source
2
J'aime votre fonction pull. Je voudrais simplement ajouter une simplification pour les cas où il n'y a qu'une seule 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]] }vous pouvez donc y aller aveciris2 %>% pull()
Rappster
7
Vous pouvez également utiliser l' magrittropérateur d'exposition ( %$%) pour extraire un vecteur d'un bloc de données. ie iris2 %>% select(Species) %>% collect() %$% Species.
Seasmith
@ Luke1018, vous devriez créer une réponse à partir de ce commentaire
rrs
pull()sera implémenté dans la version 0.6 de dplyr github.com/tidyverse/dplyr/commit/…
nacnudus
72

Vous pouvez également utiliser unlistce que je trouve plus facile à lire car vous n'avez pas besoin de répéter le nom de la colonne ou de spécifier l'index.

iris2 %>% select(Species) %>% unlist(use.names = FALSE)
StanislawSwierc
la source
1
Cela semble la méthode la plus polyvalente car elle fonctionne de manière identique avec les vecteurs et les data.frames, c'est-à-dire qu'elle permet aux fonctions d'être plus agnostiques.
geotheory
Je cherchais juste une réponse à cette question exacte et unlistc'est précisément ce dont j'avais besoin. Merci!
Andrew Brēza
unlistpeut également extraire des valeurs de plusieurs colonnes (combinant toutes les valeurs dans un seul vecteur), tout en dplyr::pullétant limité à une seule colonne.
filups21
21

J'utiliserais la extract2fonction de commodité de magrittr:

library(magrittr)
library(dplyr)

iris2 %>%
  select(Species) %>%
  extract2(1)  
Hugh
la source
Vouliez-vous utiliser collect()entre selectet extract2?
nacnudus
10
use_series(Species)est peut-être encore plus lisible. Merci de m'avoir alerté sur ces fonctions, il y en a plusieurs autres pratiques d'où cela vient.
nacnudus
20

J'écrirais probablement:

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

Puisque dplyr est conçu pour travailler avec des tbls de données, il n'y a pas de meilleur moyen d'obtenir une seule colonne de données.

hadley
la source
Je ne peux pas dire plus juste que ça. Il est apparu de manière interactive dans la console lorsque j'ai essayé d'utiliser unique (table $ column) pour vérifier les valeurs fausses.
nacnudus
4
@nacnudus pour ce cas, vous pouvez également le fairegroup_by(column) %.% tally()
hadley
12
Un argument drop = TRUEde dplyr::selectserait incroyable pour les très nombreux cas d'utilisation où nous avons réellement besoin pour extraire les vecteurs.
Antoine Lizée
C'était la seule façon pour moi d'obtenir une colonne de mon Sparklyr sdf. Pull ne fonctionnait pas pour moi sur la version 0.7.8.
Meep
16

@ Luke1018 a proposé cette solution dans l'un des commentaires:

Vous pouvez également utiliser l' magrittropérateur d'exposition ( %$%) pour extraire un vecteur d'un bloc de données.

Par exemple:

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

J'ai pensé qu'il méritait sa propre réponse.

rrs
la source
Je cherchais ceci.
Diego-MX
Comment ferais-je cela si je ne veux pas transmettre le nom de la colonne lui-même mais une variable de chaîne qui le contient?
mzuba
@mzuba tibble(x = 1:10, y = letters[1:10]) %>% select_("x") %>% unlist()et vous pouvez également en ajouter un autre %>% unname()à la fin si vous le souhaitez, mais pour mes besoins, je n'ai pas trouvé que le dernier maillon de la chaîne de tuyaux était nécessaire. Vous pouvez également spécifier use.names = FALSEdans la unlist()commande, qui fait la même chose que l'ajout unname()sur la chaîne de tuyaux.
Mark White
1
@mzuba J'utiliserais la pullcommande maintenant. Ma solution a été écrite avant la dplyrversion 0.6.
rrs
1
Notez que les %$%travaux sur une liste, alors que pull()ne le fait pas
wint3rschlaefer
3

Si vous avez l'habitude d'utiliser des crochets pour l'indexation, une autre option consiste simplement à envelopper l'approche d'indexation habituelle dans un appel à deframe () , par exemple:

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

Cela et pull () sont tous deux de très bons moyens d'obtenir une colonne tibble.

Keith Hughitt
la source