Trích xuất một cột dplyr tbl như một vector


175

Có cách nào ngắn gọn hơn để lấy một cột của một dplyr tbl làm vector, từ một tbl với back-end cơ sở dữ liệu (tức là khung / bảng dữ liệu không thể được đặt trực tiếp vào tập hợp con)?

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

Điều đó đã quá dễ dàng, vì vậy

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

Nhưng có vẻ hơi vụng về.


collect(iris2)$Speciesít vụng về?
CJ Yetman

Câu trả lời:


178

Với dplyr 0.7.0, bạn có thể sử dụng pullđể lấy vectơ từ 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

Theo nhận xét từ @nacnudus, có vẻ như một pullchức năng đã được triển khai trong dplyr 0.6:

iris2 %>% pull(Species)

Đối với các phiên bản cũ hơn của dplyr, đây là một chức năng gọn gàng để giúp kéo ra một cột đẹp hơn một chút (dễ gõ hơn và dễ đọc hơn):

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

Điều này cho phép bạn thực hiện một trong những điều sau:

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

Kết quả là ...

 [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

Và nó cũng hoạt động tốt với các khung dữ liệu:

> 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

Một cách hay để làm điều này trong v0.2 của dplyr:

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

Hoặc nếu bạn thích:

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

Hoặc nếu bàn của bạn không quá lớn, chỉ cần ...

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

2
Tôi thích chức năng kéo của bạn. Tôi chỉ cần thêm một đơn giản hóa cho các trường hợp chỉ có một biến: 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]] }vì vậy bạn có thể đi vớiiris2 %>% pull()
Rappster

7
Bạn cũng có thể sử dụng magrittrtoán tử giải thích ( %$%) để kéo một vectơ từ khung dữ liệu. tức iris2 %>% select(Species) %>% collect() %$% Species.
thợ đi biển

@ Luke1018 bạn nên tạo một câu trả lời từ nhận xét này
rrs

pull()sẽ được triển khai trong phiên bản dplyr 0.6 github.com/tidyverse/dplyr/commit/
triệt

72

Bạn cũng có thể sử dụng unlistnhững gì tôi thấy dễ đọc hơn vì bạn không cần lặp lại tên của cột hoặc chỉ định chỉ mục.

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

1
Đây có vẻ là phương pháp linh hoạt nhất vì nó hoạt động giống hệt với vectơ và data.frames, tức là nó cho phép các hàm trở nên bất khả tri hơn.
địa lý

Tôi chỉ tìm kiếm một câu trả lời cho câu hỏi chính xác này và unlistchính xác là những gì tôi cần. Cảm ơn!
Andrew Brēza

unlistcũng có thể trích xuất các giá trị từ nhiều cột (kết hợp tất cả các giá trị thành một vectơ), trong khi dplyr::pullbị giới hạn ở một cột.
quay phim21

21

Tôi sẽ sử dụng extract2chức năng tiện lợi từ magrittr:

library(magrittr)
library(dplyr)

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

Ý của bạn là sử dụng collect()giữa selectextract2?
xà cừ

10
use_series(Species)có lẽ còn dễ đọc hơn Cảm ơn đã cảnh báo tôi về các chức năng này, có một số chức năng tiện dụng khác đến từ đó.
xà cừ

20

Tôi có thể viết:

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

Vì dplyr được thiết kế để làm việc với các tbl dữ liệu, không có cách nào tốt hơn để có được một cột dữ liệu.


Không thể nói công bằng hơn thế. Nó phát sinh tương tác trong bảng điều khiển khi tôi thử sử dụng duy nhất (cột $ cột) để kiểm tra các giá trị giả.
xà cừ

4
@nacnudus cho trường hợp đó bạn cũng có thể làmgroup_by(column) %.% tally()
hadley

12
Một cuộc tranh cãi drop = TRUEđến dplyr::selectsẽ là tuyệt vời cho các trường hợp sử dụng khá nhiều nơi chúng tôi thực sự cần phải trích xuất các vector.
Antoine Lizée

Đây là cách duy nhất tôi có thể lấy một cột ra khỏi sdf Sparklyr của mình. Pull không hoạt động với tôi trên phiên bản 0.7.8.
Meep

16

@ Luke1018 đề xuất giải pháp này trong một trong những ý kiến:

Bạn cũng có thể sử dụng magrittrtoán tử giải thích ( %$%) để kéo một vectơ từ khung dữ liệu.

Ví dụ:

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

Tôi nghĩ rằng nó xứng đáng câu trả lời của riêng mình.


Tôi vẫn đang tìm kiếm điều này.
Diego-MX

Làm thế nào tôi có thể làm điều này nếu tôi muốn vượt qua không phải chính colname mà là một biến chuỗi chứa nó?
mzuba

@mzuba tibble(x = 1:10, y = letters[1:10]) %>% select_("x") %>% unlist()và bạn cũng có thể thêm một cái khác %>% unname()vào cuối nếu bạn muốn, nhưng với mục đích của tôi, tôi đã không thấy rằng liên kết chuỗi ống cuối cùng là cần thiết. Bạn cũng có thể chỉ định use.names = FALSEtrong unlist()lệnh, thao tác này cũng tương tự như thêm unname()vào chuỗi ống.
Đánh dấu trắng

1
@mzuba Tôi sẽ sử dụng pulllệnh ngay bây giờ. Giải pháp của tôi đã được viết trước dplyrphiên bản 0.6.
RRS

1
Lưu ý rằng %$%hoạt động trên bất kỳ danh sách nào, trong khi pull()không
wint3rschlaefer

2

Nếu bạn quen sử dụng dấu ngoặc vuông để lập chỉ mục, một tùy chọn khác là chỉ bao bọc cách tiếp cận lập chỉ mục thông thường trong lệnh gọi tới deframe () , ví dụ:

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

Điều đó và pull () đều là những cách khá hay để có được một cột gọn gàng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.