Dize vektör girdisini kullanarak dplyr'de birden çok sütuna göre gruplama


157

Plyr anlayışımı dplyr'a aktarmaya çalışıyorum, ancak birden çok sütuna göre nasıl gruplanacağını anlayamıyorum.

# make data with weird column names that can't be hard coded
data = data.frame(
  asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE),
  a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE),
  value = rnorm(100)
)

# get the columns we want to average within
columns = names(data)[-3]

# plyr - works
ddply(data, columns, summarize, value=mean(value))

# dplyr - raises error
data %.%
  group_by(columns) %.%
  summarise(Value = mean(value))
#> Error in eval(expr, envir, enclos) : index out of bounds

Plyr örneğini bir dplyr-esque sözdizimine çevirmek için ne eksik?

Edit 2017 : Dplyr güncellendi, böylece daha basit bir çözüm mevcut. Şu anda seçili cevaba bakın.


3
Sadece en iyi google olduğu gibi buraya var. group_by_Şimdi açıklandığı gibi kullanabilirsinizvignette("nse")
James Owers

3
@kungfujam: Sütun çiftine göre değil, sadece ilk sütuna göre gruplanıyor gibi görünüyor
sharoz

1
Kullanmanız gerekiyor .dots. İşte @hadley'nin cevabından uyarlanmış çözüm:df %>% group_by_(.dots=list(quote(asihckhdoydk), quote(a30mvxigxkgh))) %>% summarise(n = n())
James Owers

1
Aşağıdaki kodun tam kodunu
yazdınız

1
Birisi yorumun cevabında işaret ettiği gibi, amaç sabit kodlu sütun isimleri gerektirmemek.
sharoz

Yanıtlar:


52

Bu soru yayınlandığından beri, dplyr group_by( burada dokümantasyon ) kapsamlarının sürümlerini ekledi . Bu select, aşağıdaki gibi aynı işlevleri kullanmanıza olanak tanır :

data = data.frame(
    asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE),
    a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE),
    value = rnorm(100)
)

# get the columns we want to average within
columns = names(data)[-3]

library(dplyr)
df1 <- data %>%
  group_by_at(vars(one_of(columns))) %>%
  summarize(Value = mean(value))

#compare plyr for reference
df2 <- plyr::ddply(data, columns, plyr::summarize, value=mean(value))
table(df1 == df2, useNA = 'ifany')
## TRUE 
##  27 

Örnek sorunuzun çıktısı beklendiği gibidir (yukarıdaki kat ve aşağıdaki çıktı ile karşılaştırmaya bakın):

# A tibble: 9 x 3
# Groups:   asihckhdoydkhxiydfgfTgdsx [?]
  asihckhdoydkhxiydfgfTgdsx a30mvxigxkghc5cdsvxvyv0ja       Value
                     <fctr>                    <fctr>       <dbl>
1                         A                         A  0.04095002
2                         A                         B  0.24943935
3                         A                         C -0.25783892
4                         B                         A  0.15161805
5                         B                         B  0.27189974
6                         B                         C  0.20858897
7                         C                         A  0.19502221
8                         C                         B  0.56837548
9                         C                         C -0.22682998

Not beri dplyr::summarizebir defada sadece gruplama biri tabakasının kapalı şeritler, hala var bazı (bazen satır aşağı sonradan sürpriz insanları yakalamak olan) çıkan Tibble oluyor gruplama. Beklenmedik gruplama davranışından kesinlikle güvende olmak istiyorsanız, %>% ungroupözetledikten sonra her zaman ardışık düzeninize ekleyebilirsiniz .


0.7.0teklif-unquote sistemi birkaç sütun ile kullanılabilir hale getirmek için güncelleme ?
JelenaČuklina

4
Ayrıca kullanabilirsiniz .dotsargümanlar group_by()gibi: data %>% group_by(.dots = columns) %>% summarize(value = mean(value)).
Paul Rougieux

one_of()Burada bir şey yapma çağrısı var mı? Ifade bir çağrı sarılmış gibi, bu bağlamda gereksiz olduğunu düşünüyorum vars().
knowah

@Khashir evet, bu cevap hala çalışıyor @knowah Haklısın, one_of()bu bağlamda çağrı gereksiz
Empiromancer

2
@Sos Bir işlevi selectsözdizimi kullanarak birden çok sütuna uygulamak için yeni acrossişleve bakın: dplyr.tidyverse.org/reference/across.html Sizin durumunuzda şöyle görünecektirsummarize(across(all_of(c(''value_A", "value_B")), mean))
Empiromancer

102

Kodu tam olarak yazabilmek için, Hadley'in yeni sözdizimi ile ilgili cevabı hakkında bir güncelleme:

library(dplyr)

df <-  data.frame(
    asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE),
    a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE),
    value = rnorm(100)
)

# Columns you want to group by
grp_cols <- names(df)[-3]

# Convert character vector to list of symbols
dots <- lapply(grp_cols, as.symbol)

# Perform frequency counts
df %>%
    group_by_(.dots=dots) %>%
    summarise(n = n())

çıktı:

Source: local data frame [9 x 3]
Groups: asihckhdoydk

  asihckhdoydk a30mvxigxkgh  n
1            A            A 10
2            A            B 10
3            A            C 13
4            B            A 14
5            B            B 10
6            B            C 12
7            C            A  9
8            C            B 12
9            C            C 10

1
Bu, sütun adlarını hala kodlama yapıyor gibi görünüyor, bunun yerine bir formülde. Sorunun amacı, yazmak zorunda kalmamak için dizelerin nasıl kullanılacağıdır asihckhdoydk...
Gregor Thomas

1
Argüman dots <- lapply(names(df)[-3], function(x) as.symbol(x))oluşturmak için çözümü güncelledi.dots
James Owers

4
bu cevapları sıralamaya çalışmak .dots=çok önemli bir adımdı. Birisi group_byçağrıda neden gerekli olduğunu iyi biliyorsa, bu yanıtı düzenleyebilir misiniz? şu anda biraz anlaşılmaz.
Andrew

12
vignette("nse"), alıntı yapmanın kabul edilebilir üç yolu olduğunu gösterir: formül, fiyat teklifi ve karakter. Hangi ortamdan çekileceği konusunda endişe duymadığınız sürece, muhtemelen uzaklaşabilirsinizgroup_by_(.dots=grp_cols)
Ari B. Friedman

58

Bu dplyr için destek şu anda oldukça zayıf, sonunda ben sözdizimi gibi bir şey olacağını düşünüyorum:

df %.% group_by(.groups = c("asdfgfTgdsx", "asdfk30v0ja"))

Ama bu muhtemelen bir süre orada olmayacak (çünkü tüm sonuçları düşünmem gerekiyor).

Bu arada, regroup()sembollerin bir listesini alan kullanabilirsiniz :

library(dplyr)

df <-  data.frame(
  asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE),
  a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE),
  value = rnorm(100)
)

df %.%
  regroup(list(quote(asihckhdoydk), quote(a30mvxigxkgh))) %.%
  summarise(n = n())

Sütun adlarının bir karakter vektör var varsa, doğru yapıya çevirebiliriz lapply()ve as.symbol():

vars <- setdiff(names(df), "value")
vars2 <- lapply(vars, as.symbol)

df %.% regroup(vars2) %.% summarise(n = n())

6
as.symbolçözer. Teşekkürler! Gelişime yardımcı olması durumunda: bu senaryo benim için gerçekten yaygın bir senaryodur. Diğer değişkenlerin her birleşimi üzerinde sayısal bir sonuç toplayın.
Ocak'ta sharoz

Görünüşe göre bu sadece bu örnek için çalışıyor ve başka hiçbir şey için değil.
Paulo E. Cardoso

3
Aslında bunu cevap olarak işaretledim, ancak dplyr güncellemeleri kungfujam'ın cevabının çalışmasına izin veriyor.
sharoz

regroupaynı zamanda kullanımdan kaldırılmıştır (en azından sürüm 0.4.3'ten itibaren).
Berk U.

27

İçindeki sütunların dize belirtimi dplyr artık dplyralt çizgi içinde isimleri biten işlevlerin varyantları tarafından desteklenmektedir . Örneğin, group_byişleve karşılık olarak, group_by_dize bağımsız değişkenleri alabilen bir işlev vardır. Bu vinyet , bu işlevlerin sözdizimini ayrıntılı olarak açıklamaktadır.

Aşağıdaki kod parçası, @sharoz'un asıl oluşturduğu sorunu temiz bir şekilde çözmektedir ( .dotsargümanı yazma gereğini not edin ):

# Given data and columns from the OP

data %>%
    group_by_(.dots = columns) %>%
    summarise(Value = mean(value))

(Dplyr'in şimdi %>%operatörü kullandığını ve %.%kullanımdan kaldırıldığını unutmayın).


17

Dplyr dize argümanları için tam desteğe sahip olana kadar, belki de bu öz yararlıdır:

https://gist.github.com/skranz/9681509

Dize argümanlarını kullanan s_group_by, s_mutate, s_filter, vb. Bunları normal dplyr işlevleriyle karıştırabilirsiniz. Örneğin

cols = c("cyl","gear")
mtcars %.%
  s_group_by(cols) %.%  
  s_summarise("avdisp=mean(disp), max(disp)") %.%
  arrange(avdisp)

11

Bir karakter vektöründen ziyade nesneleri iletirseniz çalışır (iyi değilsiniz, ama ...):

df %.%
    group_by(asdfgfTgdsx, asdfk30v0ja) %.%
    summarise(Value = mean(value))

> df %.%
+   group_by(asdfgfTgdsx, asdfk30v0ja) %.%
+   summarise(Value = mean(value))
Source: local data frame [9 x 3]
Groups: asdfgfTgdsx

  asdfgfTgdsx asdfk30v0ja        Value
1           A           C  0.046538002
2           C           B -0.286359899
3           B           A -0.305159419
4           C           A -0.004741504
5           B           B  0.520126476
6           C           C  0.086805492
7           B           C -0.052613078
8           A           A  0.368410146
9           A           B  0.088462212

nerede dfoldu senindata .

?group_by diyor:

 ...: variables to group by. All tbls accept variable names, some
      will also accept functons of variables. Duplicated groups
      will be silently dropped.

hangi isimlerin karakter versiyonları değil, bunlara nasıl atıfta bulunacağınız anlamına gelir foo$bar; barburada alıntılanmaz. Veya bir formüldeki değişkenlere nasıl başvuracağınız:foo ~ bar .

@Arun ayrıca şunları yapabileceğinizden de bahseder:

df %.%
    group_by("asdfgfTgdsx", "asdfk30v0ja") %.%
    summarise(Value = mean(value))

Ama değerlendirilmemiş bir şeye geçemezsiniz veri nesnesindeki bir değişkenin adı olmayan bir .

Bunun Hadley'in ...argüman aracılığıyla aktardığınız şeyleri aramak için kullandığı dahili yöntemlerden kaynaklandığını düşünüyorum .


1
@Arun Bunun için teşekkürler. Bunu fark etmemiştim, ama bu da mantıklı. Bu konuda size ve yorumunuza atıfta bulunan bir not ekledim.
Gavin Simpson

4
Ne yazık ki, sütun adlarını zor kodlamaya güvenemiyorum. Bunu belirtmek zorunda kalmadan yapmaya çalışıyorum.
Ocak'ta sharoz

4
data = data.frame(
  my.a = sample(LETTERS[1:3], 100, replace=TRUE),
  my.b = sample(LETTERS[1:3], 100, replace=TRUE),
  value = rnorm(100)
)

group_by(data,newcol=paste(my.a,my.b,sep="_")) %>% summarise(Value=mean(value))

4

Burada açıklamak istediğim cevaplardan eksik olan bir (küçük) vaka, gruplanacak değişkenlerin bir boru hattında dinamik olarak orta akışta oluşturulduğu zamandır:

library(wakefield)
df_foo = r_series(rnorm, 10, 1000)
df_foo %>% 
  # 1. create quantized versions of base variables
  mutate_each(
    funs(Quantized = . > 0)
  ) %>% 
  # 2. group_by the indicator variables
  group_by_(
    .dots = grep("Quantized", names(.), value = TRUE)
    ) %>% 
  # 3. summarize the base variables
  summarize_each(
    funs(sum(., na.rm = TRUE)), contains("X_")
  )

Bu temelde bunu başarmak için grepbirlikte nasıl kullanılacağını gösterir group_by_(.dots = ...).


3

Bağımsız .dotsdeğişkenin dplyr::group_byişleve karakter vektör girişi olarak kullanılmasına ilişkin genel örnek :

iris %>% 
    group_by(.dots ="Species") %>% 
    summarise(meanpetallength = mean(Petal.Length))

Veya gruplama değişkeni için sabit bir kod adı olmadan (OP tarafından sorulduğu gibi):

iris %>% 
    group_by(.dots = names(iris)[5]) %>% 
    summarise_at("Petal.Length", mean)

OP örneği ile:

data %>% 
    group_by(.dots =names(data)[-3]) %>% 
    summarise_at("value", mean)

Ayrıca zamirleri, quasiquotation, quosures ve tidyeval açıklayan programlama dplyr vinyet .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.