Question Réduire les lignes dans un bloc de données en utilisant R


J'ai un bloc de données en R défini comme suit:

trame de données:

col 1col 2col 3col4
  200AIG8.512
 800AIG8.120.1
500A1B2050,5
800A1B1230
120A2M1.68.5

dat <- structure(list(col1 = c(200, 800, 500, 800, 120), col2 = structure(c(3L, 
    3L, 1L, 1L, 2L), .Label = c("A1B", "A2M", "AIG"), class = "factor"), 
        col3 = c(8.5, 8.1, 20, 12, 1.6), col4 = c(12, 20.1, 50.5, 
        30, 8.5)), .Names = c("col1", "col2", "col3", "col4"), row.names = c(NA, 
    -5L), class = "data.frame")

Ensuite, je voudrais réduire les lignes par id (dans ce cas, les identifiants uniques sont A1G, A1B, A2M).
Col 1, je voudrais le réduire en ajoutant les lignes avec le même identifiant.
Col 2, je voudrais le réduire à chaque identifiant unique
Col 3, je voudrais le réduire comme suit, prendre col1 * col3, les ajouter, puis les diviser par la somme de col1.
 Ainsi, la valeur de la nouvelle ligne A1G devrait être (8,5 * 20 + 8,1 * 80) / (80 + 20). Aka la moyenne pondérée de la colonne 3 pondérée par les valeurs de col1.
Col 4, je voudrais prendre la valeur maximale.

Le bloc de données résultant devrait ressembler à:

colonne 1colonne 2colonne 3colonne 4
  800 + 200 = 1000AIG(8.5 * 200 + 8.1 * 800) /1000=8.18max (12,20.1) = 20,1
 800 + 500 = 1300AIB(20 * 800 + 12 * 500) /1300=16.9max (50,5, 30) = 50,5
120A2M1.68.5

Aucune suggestion?


13
2017-10-14 17:30


origine


Réponses:


Voici une solution data.table qui évoluera bien pour le big data (vitesse et mémoire efficaces)

library(data.table)
DT <- data.table(dat, key="col2")
DT[, list(col1=sum(col1), 
          col3=sum(col1 * col3) / sum(col1), 
          col4=max(col4)), by=col2]
#   col2 col1     col3 col4
#1:  A1B 1300 15.07692 50.5
#2:  A2M  120  1.60000  8.5
#3:  AIG 1000  8.18000 20.1

19
2017-10-14 17:48



Une solution en base:

dat2<-do.call(rbind,
  by(dat,dat$col2, function(x) 
    with (x,
     data.frame(
       col1 = sum(col1),
       col3 = sum(col1 * col3) / sum(col1),
       col4 = max(col4)
     )
    )
  )
)
dat2$col2<-rownames(dat2)

#     col1     col3 col4 col2
# A1B 1300 15.07692 50.5  A1B
# A2M  120  1.60000  8.5  A2M
# AIG 1000  8.18000 20.1  AIG

9
2017-10-14 18:00



En utilisant le plyr paquet:

library(plyr)
ddply(df, "col2", summarize, col1 = sum(col1),
                             col3 = sum(col1 * col3) / sum(col1),
                             col4 = max(col4))
#   col2 col1     col3 col4
# 1  A1B 1300 15.07692 50.5
# 2  A2M  120  1.60000  8.5
# 3  AIG 1000  8.18000 20.1

6
2017-10-14 17:41



Une solution de base mais j'aime la solution data.table:

dat[, 2] <- factor(dat[, 2], levels=unique(dat[, 2])) #in case not already ordered
L1 <- split(dat, dat$col2)                            #split into list by col2

funny <- function(x){                                 #function to calculate stuff
    x <- data.frame(x)
    c(col1=sum(x[,1]), col2=as.character(x[1, 2]), 
        col3=sum((x[, 3]*x[, 1]))/sum(x[, 1]),
        col4=max(x[,4]))
}

#apply function and wrap it up into dataframe
dat2 <- data.frame(do.call(rbind, lapply(L1, funny)), row.names=NULL) 
dat2[, -2] <- apply(dat2[, -2], 2, as.numeric)       #reapply classes    
dat2

#> dat2
#  col1 col2     col3 col4
#1 1000  AIG  8.18000 20.1
#2 1300  A1B 15.07692 50.5
#3  120  A2M  1.60000  8.5

1
2017-10-14 18:01