R में समूह द्वारा डेटा का सारांश कैसे दिया जाए? [बन्द है]


181

मेरे पास इस तरह से आर डेटा फ्रेम है:

        age group
1   23.0883     1
2   25.8344     1
3   29.4648     1
4   32.7858     2
5   33.6372     1
6   34.9350     1
7   35.2115     2
8   35.2115     2
9   35.2115     2
10  36.7803     1
...

मुझे निम्नलिखित फ़ॉर्म में डेटा फ़्रेम प्राप्त करने की आवश्यकता है:

group mean     sd
1     34.5     5.6
2     32.3     4.2
...

समूह संख्या भिन्न हो सकती है, लेकिन उनके नाम और मात्रा को कॉल करके प्राप्त किया जा सकता है levels(factor(data$group))

परिणाम प्राप्त करने के लिए डेटा के साथ क्या हेरफेर किया जाना चाहिए?


परिणाम डेटा फ़्रेम में अल्पविराम का मतलब कुछ विशेष है, या यह केवल दशमलव बिंदु है?

@mpiktas ध्यान देने के लिए धन्यवाद। सही किया। ये स्थानीय मुद्दे थे (मैं रूसी हूं) - हम दशमलव पृथक्करण के लिए अल्पविराम का उपयोग करते हैं।
यूरी पेत्रोव्स्की

3
मुझे उस पर शक हुआ। पूरे यूरोप में अंग्रेजों को छोड़कर अल्पविराम का उपयोग किया जाता है।
mpiktas

4
ब्रिटिश नहीं होने के बावजूद, मैं दशमलव विभाजक के लिए डॉट पसंद करता हूं।
रोमन लुसट्रिक

1
इस प्रकार के किसी भी बाद के कोडिंग प्रश्नों के लिए देखें aggregate, tapplyऔर फिर stackoverflow.com
conjugateprior

जवाबों:


140

यहाँ है plyr एक लाइन का उपयोग संस्करण ddply :

dt <- data.frame(age=rchisq(20,10),group=sample(1:2,20,rep=T))
ddply(dt,~group,summarise,mean=mean(age),sd=sd(age))

यहां नया पैकेज data.table का उपयोग करके एक और एक लाइन संस्करण है ।

dtf <- data.frame(age=rchisq(100000,10),group=factor(sample(1:10,100000,rep=T)))
dt <- data.table(dtf)
dt[,list(mean=mean(age),sd=sd(age)),by=group]

यह एक तेज है, हालांकि यह केवल 100k पंक्तियों वाली तालिका पर ध्यान देने योग्य है। 2.53 Ghz कोर 2 डुओ प्रोसेसर और R 2.11.1 के साथ मेरी मैकबुक प्रो पर टाइमिंग:

> system.time(aa <- ddply(dtf,~group,summarise,mean=mean(age),sd=sd(age)))
utilisateur     système      écoulé 
      0.513       0.180       0.692 
> system.time(aa <- dt[,list(mean=mean(age),sd=sd(age)),by=group])
utilisateur     système      écoulé 
      0.087       0.018       0.103 

यदि हम उपयोग करते हैं तो आगे की बचत संभव है setkey:

> setkey(dt,group)
> system.time(dt[,list(mean=mean(age),sd=sd(age)),by=group])
utilisateur     système      écoulé 
      0.040       0.007       0.048 

2
@chl, इसने मुझे इस नए data.table पैकेज को आज़माने का मौका दिया । यह वास्तव में आशाजनक लगता है।
म्पिकटस

7
Data.table के लिए +6000। यह वास्तव में ddply की तुलना में बहुत तेज है, यहां तक ​​कि मेरे लिए 100k से छोटे डेटासेट पर (मेरे पास सिर्फ 20k पंक्तियों के साथ एक है)। मेरे द्वारा लागू किए जा रहे फ़ंक्शंस के साथ कुछ करने के लिए ज़रूर होना चाहिए, लेकिन कुछ ही सेकंड्स में ddply मिनट और data.table होगी।
परमाणु ऊर्जा

सरल टाइपो: मुझे लगता है कि आप दूसरे कोड ब्लॉक के dt <- data.table(dtf)बजाय इसका मतलब है dt <- data.table(dt)। इस तरह, आप पैकेज dtसे फ़ंक्शन के बजाय डेटा फ़्रेम से डेटा तालिका बना रहे हैं stats। मैंने इसे संपादित करने की कोशिश की, लेकिन मैं छह पात्रों के तहत संपादन नहीं कर सकता।
क्रिस्टोफर बॉटम्स

मेरे (इस मामले में विनम्र नहीं) राय data.tableडेटा एकत्र करने का सबसे अच्छा तरीका है और यह जवाब बहुत अच्छा है, लेकिन अभी भी सतह को खरोंचता है। सिंटैक्टिक रूप से बेहतर होने के अलावा, यह बेहद लचीला भी है और इसमें कई उन्नत विशेषताएं हैं जो जुड़ती हैं और आंतरिक यांत्रिकी शामिल हैं। अधिक जानकारी के लिए अक्सर पूछे जाने वाले प्रश्न, जीथब पृष्ठ या पाठ्यक्रम देखें।
जीनोरमा

97

एक संभावना कुल कार्य का उपयोग करना है । उदाहरण के लिए,

aggregate(data$age, by=list(data$group), FUN=mean)[2]

आपको वांछित परिणाम का दूसरा कॉलम देता है।


1
अपने स्थानीय सहायता सर्वर :-) +1 से लिंक न करें, लेकिन @ स्टीफ़न की प्रतिक्रिया पर मेरी टिप्पणी देखें।
CHL

फोन करके बात पूरी कर ली, data.frame(group=levels(factor(data$group)),mean=(aggregate(data$age, by=list(data$group), FUN=mean)$x),sd=(aggregate(data$age, by=list(data$group), FUN=sd)$x))लेकिन मुझे नहीं लगता कि यह सही तरीका है। मुझे यकीन नहीं है कि क्या होगा फिर बंधे हुए कॉलम के परिणाम अलग-अलग क्रम में होंगे (मुझे लगता है कि यह संभव है)। तुम्हारा क्या जुल्म है?
यूरी पेत्रोव्स्की

9
@Yuriy पंक्तियों को क्रम से बाहर नहीं होना चाहिए, लेकिन यहां एक तरीका यह है कि इसे कॉल करें aggregate():aggregate(age ~ group, data=dat, FUN = function(x) c(M=mean(x), SD=sd(x)))
Lockoff

@lockedoff: मेरा जवाब पूरा करने के लिए धन्यवाद!
ओकराम

27

चूंकि आप डेटा फ़्रेम में हेरफेर कर रहे हैं, इसलिए dplyrपैकेज संभवतः इसे करने का सबसे तेज़ तरीका है।

library(dplyr)
dt <- data.frame(age=rchisq(20,10), group=sample(1:2,20, rep=T))
grp <- group_by(dt, group)
summarise(grp, mean=mean(age), sd=sd(age))

या समकक्ष, dplyr/ magrittrपाइप ऑपरेटर का उपयोग कर :

library(dplyr)
dt <- data.frame(age=rchisq(20,10), group=sample(1:2,20, rep=T))
group_by(dt, group) %>%
 summarise(mean=mean(age), sd=sd(age))

EDIT पाइप ऑपरेटर का पूरा उपयोग:

library(dplyr)
data.frame(age=rchisq(20,10), group=sample(1:2,20, rep=T)) %>%
  group_by(group) %>%
  summarise(mean=mean(age), sd=sd(age))

3
के लिए +1 dplyr। इसने बहुत सारे R कार्यों को सरल बनाया है और इनमें से कई तरीके अप्रचलित हैं।
ग्रागमैक्फर्लेन

पाइप ऑपरेटर संस्करण का पूर्ण उपयोग मेरे लिए दुर्भाग्य से काम नहीं करता है
dagcilibili

क्या आपने despr या magrittr को लोड किया है?
बस्तियान क्वैस्ट

समाधान की ओर इशारा करने के लिए बहुत बहुत धन्यवाद @bquast, plyrइसके बजाय संक्षेप समारोह को बुलाया dplyrगया था जिससे समस्या पैदा हो रही थी।
dagcilibili

12

बहुत, धन्यवाद dplyr समाधान जोड़ने के लिए धन्यवाद!

तब पता चलता है कि, dplyr और data.table बहुत करीब हैं:

library(plyr)
library(dplyr)
library(data.table)
library(rbenchmark)

dtf <- data.frame(age=rchisq(100000,10),group=factor(sample(1:10,100000,rep=T)))
dt <- data.table(dtf)

setkey(dt,group)

a<-benchmark(ddply(dtf,~group,plyr:::summarise,mean=mean(age),sd=sd(age)),
         dt[,list(mean=mean(age),sd=sd(age)),by=group],
         group_by(dt, group) %>% summarise(mean=mean(age),sd=sd(age) ),
         group_by(dtf, group) %>% summarise(mean=mean(age),sd=sd(age) )
)

a[, c(1,3,4)]

data.table अभी भी सबसे तेज़ है, इसके बाद बहुत ही निकटता से dplyr (), जो दिलचस्प रूप से डेटा पर तेजी से लगता है। data.table की तुलना में:

                                                              test elapsed relative
1 ddply(dtf, ~group, plyr:::summarise, mean = mean(age), sd = sd(age))   1.689    4.867
2               dt[, list(mean = mean(age), sd = sd(age)), by = group]   0.347    1.000
4   group_by(dtf, group) %>% summarise(mean = mean(age), sd = sd(age))   0.369    1.063
3    group_by(dt, group) %>% summarise(mean = mean(age), sd = sd(age))   0.580    1.671

पहले तो मुझे लगा कि आपको सेटमार्क को बेंचमार्क में स्थानांतरित करने की आवश्यकता है, लेकिन यह पता चला कि लगभग बिल्कुल भी समय नहीं है।
kasterma

10

मौजूदा सुझावों के अलावा, आप इस describe.byफ़ंक्शन को देखना चाहेंगेpsych पैकेज ।

यह एक समूहीकरण चर पर आधारित माध्य और मानक विचलन सहित कई वर्णनात्मक आंकड़े प्रदान करता है।


यह अच्छा है, लेकिन कुछ हद तक LaTeX IME को निर्यात करना मुश्किल है।
रिचमीमोर्रोसो

10

मैंने doBy पैकेजsummaryBy में फ़ंक्शन को इसके लिए सबसे सुविधाजनक माना है:

library(doBy)

age    = c(23.0883, 25.8344, 29.4648, 32.7858, 33.6372,
           34.935,  35.2115, 35.2115,  5.2115, 36.7803)
group  = c(1, 1, 1, 2, 1, 1, 2, 2, 2, 1)
dframe = data.frame(age=age, group=group)

summaryBy(age~group, data=dframe, FUN=c(mean, sd))
# 
#   group age.mean    age.sd
# 1     1 30.62333  5.415439
# 2     2 27.10507 14.640441

9

sqldfपैकेज का उपयोग करें । यह आपको डेटा को संक्षेप में प्रस्तुत करने के लिए SQL का उपयोग करने की अनुमति देता है। एक बार लोड करने के बाद आप कुछ लिख सकते हैं जैसे -

sqldf('  select group,avg(age) from data group by group  ')

8

संपादित: chl के सुझावों के अनुसार

जिस फ़ंक्शन की आप तलाश कर रहे हैं उसे "टैपली" कहा जाता है जो एक कारक द्वारा निर्दिष्ट प्रति समूह पर एक फ़ंक्शन लागू करता है।

# create some artificial data
set.seed(42)
groups <- 5

agedat <- c()
groupdat <- c()

for(group in 1:groups){
    agedat <- c(agedat,rnorm(100,mean=0 + group,1/group))
    groupdat <- c(groupdat,rep(group,100))
}
dat <- data.frame("age"=agedat,"group"=factor(groupdat))

# calculate mean and stdev age per group
res <- rbind.data.frame(group=1:5, with(dat, tapply(age, group, function(x) c(mean(x), sd(x)))))
names(res) <- paste("group",1:5)
row.names(res)[2:3] <- c("mean","sd")

मैं वास्तव में एक बुनियादी आर ट्यूटोरियल के माध्यम से काम करने का सुझाव देता हूं जो सभी आमतौर पर उपयोग किए जाने वाले डेटास्ट्रक्चर और तरीकों की व्याख्या करता है। अन्यथा आप प्रोग्रामिंग के दौरान हर इंच अटक जाएंगे। निशुल्क उपलब्ध संसाधनों के संग्रह के लिए इस प्रश्न को देखें ।


2
@steffen +1 लेकिन forयहाँ एक लूप की कोई आवश्यकता नहीं है, आप अपने डेटाफ़्रेम इनलाइन, IMO को दूषित कर सकते हैं। के लिए tapplyकॉल, उपयोग function(x) c(mean(x),sd(x)))और cbindओपी दोनों आंकड़ों के लिए कहा के रूप में परिणाम। इसके अलावा, ddplyसे plyr पैकेज इस सुचारू रूप से कर सकता है।
CHL

@steffen समस्या यह है कि मैंने जिस तालिका संरचना का वर्णन किया है, उसकी वास्तव में आवश्यकता है। साधन और एसडी प्राप्त करने में कोई समस्या नहीं है। समस्या स्ट्रेचर को लेकर है।
यूरी पेट्रोव्स्की

@chl: आपकी टिप्पणी के लिए धन्यवाद, plyr :) के बारे में नहीं पता था। मैंने cbind जोड़ा, लेकिन बाकी अछूता छोड़ दिया। कोई दूसरा क्रेडिट ले सकता है, यह उत्तर कम इष्टतम उदाहरण के रूप में रहेगा।
स्टीफ़न

@ युरि: को जोड़ा गया cbind। यदि आप पहले से ही जानते हैं कि प्रति समूह फ़ंक्शन कैसे लागू किया जाता है, तो आप अपने प्रश्न (केवल स्पष्टता के लिए) का सुधार कर सकते हैं।
स्टीफेन

@ ostffen cbind("mean"=mperage,"stdev"=stperage) gives no 'group' column. Will be joining by cbind (समूह = स्तर (कारक (डेटा $ समूह)), "मतलब" = मेपरेज, "स्टैड" = स्टैपरेज) `सही?
युरी पेत्रोव्स्कि

7

यहाँ aggregates()कुछ समय पहले मैंने अपने आप को फंक्शन के साथ एक उदाहरण दिया है :

# simulates data
set.seed(666)
( dat <- data.frame(group=gl(3,6), level=factor(rep(c("A","B","C"), 6)), 
                    y=round(rnorm(18,10),1)) )

> dat
   group level    y
1      1     A 10.8
2      1     B 12.0
3      1     C  9.6
4      1     A 12.0
5      1     B  7.8
6      1     C 10.8
7      2     A  8.7
8      2     B  9.2
9      2     C  8.2
10     2     A 10.0
11     2     B 12.2
12     2     C  8.2
13     3     A 10.9
14     3     B  8.3
15     3     C 10.1
16     3     A  9.9
17     3     B 10.9
18     3     C 10.3

# aggregates() function
aggregates <- function(formula, data=NULL, FUNS){ 
    if(class(FUNS)=="list"){ 
        f <- function(x) sapply(FUNS, function(fun) fun(x)) 
    }else{f <- FUNS} 
    temp <- aggregate(formula, data, f) 
    out <- data.frame(temp[,-ncol(temp)], temp[,ncol(temp)]) 
    colnames(out)[1] <- colnames(temp)[1] 
return(out) 
} 

# example 
FUNS <- function(x) c(mean=round(mean(x),0), sd=round(sd(x), 0)) 
( ag <- aggregates(y~group:level, data=dat, FUNS=FUNS) ) 

यह निम्नलिखित परिणाम देता है:

> ag
  group level mean sd
1     1     A   11  1
2     2     A    9  1
3     3     A   10  1
4     1     B   10  3
5     2     B   11  2
6     3     B   10  2
7     1     C   10  1
8     2     C    8  0
9     3     C   10  0

हो सकता है कि आप आर फंक्शन स्प्लिट () से शुरू होने वाला परिणाम प्राप्त कर सकें:

> with(dat, sapply( split(y, group:level), FUNS ) )
     1:A 1:B 1:C 2:A 2:B 2:C 3:A 3:B 3:C
mean  11  10  10   9  11   8  10  10  10
sd     1   3   1   1   2   0   1   2   0

मुझे aggregatesफंक्शन के आउटपुट पर वापस आते हैं । आप इसे एक सुंदर तालिका का उपयोग करके बदल सकते हैं reshape(), xtabs()और ftable():

rag <- reshape(ag, varying=list(3:4), direction="long", v.names="y") 
rag$time <- factor(rag$time) 
ft <- ftable(xtabs(y~group+level+time, data=rag)) 
attributes(ft)$col.vars <- list(c("mean","sd")) 

यह देता है:

> ft 
             mean sd
group level         
1     A        11  1
      B        10  3
      C        10  1
2     A         9  1
      B        11  2
      C         8  0
3     A        10  1
      B        10  2
      C        10  0

सुंदर है, है ना? आप पैकेज के textplot()फ़ंक्शन के साथ इस तालिका को एक पीडीएफ में निर्यात कर सकते हैं gplots

यहाँ देखेंदूसरों के समाधान के लिए ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.