简体   繁体   中英

grouping by one column in R

I have a dataset "df"

> df
      A    B  C
1  tanu  abc 10
2  tanu  def 20
3  tanu  ghi 15
4  tanu  jkl 28
5  tanu  mno 33
6  tanu  pqr 46
7  tanu  stu 83
8  tanu  vwx 15
9   edu  yz1 60
10  edu abc2 85

> group
[1] 3 2 3 2

I have to find the maximum value of column "C" for each group. Each group is groupby column "A" containing corresponding number of rows from vector "group"

Group1:    
    tanu  abc 10
    tanu  def 20
    tanu  ghi 15
Group2:
    tanu  jkl 28
    tanu  mno 33
Group3:
    tanu  pqr 46
    tanu  stu 83
    tanu  vwx 15
Group4:
    edu  yz1 60
    edu abc2 85

I was not able to achieve this by aggregate or by function. I want my output be

> out
      A    B  C  
    tanu  def 20 
    tanu  mno 33 
    tanu  stu 83  
    edu  abc2 85

Appreciate any help. TIA.

Another base R way with by and which.max :

do.call(rbind, 
   by(df, list(rep(seq_along(group), group)), function(g) g[which.max(g$C),]))

#      A    B  C
# 1 tanu  def 20
# 2 tanu  mno 33
# 3 tanu  stu 83
# 4  edu abc2 85

At first, I thought it was the maximum or C column and the min value based on the group for B variable. Below is the solution based on that.

library(data.table)
 res <- setDT(df)[, list(B=B[min(group)], C=max(C)),
             by=list(gr=rep(seq_along(group), group),A)][,gr:=NULL]

After looking @Matthew Plourde's solution, it became evident that I was wrong (in the example, both produce the same result). In that case,

 res <- setDT(df)[df[, max(C)==C,
                by=list(rep(seq_along(group), group), A)]$V1]


 res
 #      A    B  C
 #1: tanu  def 20
 #2: tanu  mno 33
 #3: tanu  stu 83
 #4:  edu abc2 85

Or using dplyr

  library(dplyr)
  df %>% 
      group_by(gr=rep(seq_along(group), group), A) %>% 
      filter(C==max(C))%>% 
      ungroup() %>% 
      select(-gr)
   #    A    B  C
   #1 tanu  def 20
   #2 tanu  mno 33
   #3 tanu  stu 83
   #4  edu abc2 85

data

df <-  structure(list(A = c("tanu", "tanu", "tanu", "tanu", "tanu", 
"tanu", "tanu", "tanu", "edu", "edu"), B = c("abc", "def", "ghi", 
"jkl", "mno", "pqr", "stu", "vwx", "yz1", "abc2"), C = c(10L, 
20L, 15L, 28L, 33L, 46L, 83L, 15L, 60L, 85L)), .Names = c("A", 
"B", "C"), class = "data.frame", row.names = c("1", "2", "3", 
"4", "5", "6", "7", "8", "9", "10"))

I think this will also do it.

s <- sapply(split(df$C, rep.int(seq_along(group), group)), which.max)
df[s + cumsum(c(0, group[-length(group)])), ]
#       A    B  C
# 2  tanu  def 20
# 5  tanu  mno 33
# 7  tanu  stu 83
# 10  edu abc2 85

This might not be the clearest answer but it works :)

A = c("tanu", 
  "tanu",
  "tanu",
  "tanu",  
  "tanu",  
  "tanu",  
  "tanu",  
  "tanu",  
  "edu",  
  "edu")

B = c("abc", 
  "def",
  "ghi",
  "jkl",  
  "mno",  
  "pqr",  
  "stu",  
  "vwx",  
  "yz1",  
  "abc2")

C = c(10,20,15,28,33,46,83,15,60,85)
df = data.frame(A=A, B=B, C=C)
group = c(3,2,3,2)

out = NULL
line.nb = 1

for(i in 1:length(group)){

 beg = line.nb
 end = line.nb + group[i]-1
 temp = df[beg:end,]

 res = temp[which(temp[,"C"] ==max(temp[,"C"])), ] 
 out = rbind(out,res)

 line.nb = line.nb+group[i]
}

out

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM