2016-01-18 15 views
8

Ho un data.frame con molte colonne (~ 50). Alcuni di loro sono di carattere, alcuni sono numerici e 3 di essi li uso per il raggruppamento.R: riepilogare più colonne (numerico, carattere) e rimuovere NAs

devo:

  • rimuovere AN da colonne numeriche
  • calcolare la media di ciascuna delle colonne numeriche
  • estrarre il primo elemento delle colonne di caratteri

Diciamo che stiamo usando i dati dell'iride modificati come di seguito:

data(iris) 
iris$year <- rep(c(2000,3000),each=25) ## for grouping 
iris$color <- rep(c("red","green","blue"),each=50) ## character column 
iris[1,] <- NA ## introducing NAs 

Ho ~ 50 colonne in totale, numerico e carattere mescolati insieme. Ho provato qualcosa di simile:

giris <- group_by(iris, Species, year) 
cls <- unlist(sapply(giris, class)) ## find out classes 
action <- ifelse(cls == "numeric", "mean", "first") 
action <- paste(action) 
summarise_each(giris, action) 

quello che ottengo è un mezzo per tutte le colonne di un gruppo seguiti da colonne con i primi valori in rispettivo gruppo. E le NA non sono gestite ... Che non è esattamente quello che cerco ...

Aiuta qualcuno?

risposta

9

Si potrebbe provare questo con un if/else nel funs di summarise_each:

iris %>% 
    group_by(Species, year) %>% 
    summarise_each(funs(if(is.numeric(.)) mean(., na.rm = TRUE) else first(.))) 

Dal momento che avete un po 'NA' anche in colonne di raggruppamento, si potrebbe aggiungere un filter dichiarazione:

iris %>% 
    filter(!is.na(Species) & !is.na(year)) %>% 
    group_by(Species, year) %>% 
    summarise_each(funs(if(is.numeric(.)) mean(., na.rm = TRUE) else first(.))) 
#Source: local data frame [6 x 7] 
#Groups: Species [?] 
# 
#  Species year Sepal.Length Sepal.Width Petal.Length Petal.Width color 
#  (fctr) (dbl)  (dbl)  (dbl)  (dbl)  (dbl) (chr) 
#1  setosa 2000  5.025 3.479167  1.4625  0.250 red 
#2  setosa 3000  4.984 3.376000  1.4640  0.244 red 
#3 versicolor 2000  6.012 2.776000  4.3120  1.344 green 
#4 versicolor 3000  5.860 2.764000  4.2080  1.308 green 
#5 virginica 2000  6.576 2.928000  5.6400  2.044 blue 
#6 virginica 3000  6.600 3.020000  5.4640  2.008 blue 

Per evitare potenziali NA nella colonna dei colori (o qualsiasi colonna non numerica), è possibile modificarla su first(na.omit(.)).


Si potrebbe anche provare data.table:

library(data.table) 
setDT(iris) 
iris[!is.na(Species) & !is.na(year), lapply(.SD, function(x) { 
    if(is.numeric(x)) mean(x, na.rm = TRUE) else x[!is.na(x)][1L]}), 
    by = list(Species, year)] 
#  Species year Sepal.Length Sepal.Width Petal.Length Petal.Width color 
#1:  setosa 2000  5.025 3.479167  1.4625  0.250 red 
#2:  setosa 3000  4.984 3.376000  1.4640  0.244 red 
#3: versicolor 2000  6.012 2.776000  4.3120  1.344 green 
#4: versicolor 3000  5.860 2.764000  4.2080  1.308 green 
#5: virginica 2000  6.576 2.928000  5.6400  2.044 blue 
#6: virginica 3000  6.600 3.020000  5.4640  2.008 blue 
+0

Penso che potresti voler aggiungere 'na.omit()' sopra 'group_by', ma questo è l'essenza della domanda. – JasonAizkalns

+0

@JasonAizkalns, io non la penso così - questo potenzialmente rimuoverebbe molte righe che dovrebbero essere mantenute. Ma sono d'accordo sul fatto che un 'filter (! Is.na (Species) &! Is.na (year))' avrebbe senso –

+0

Questa è una soluzione veramente carina e (la più importante) che permette anche di dare un'occhiata a cosa sta andando sotto il cofano di summarise_each. Ne sono davvero, DAVVERO grato, Doncendo :-). Allo stesso tempo, pensi che ci possa essere un modo per accelerarlo? – rpl

0

dò una prova:

1. Per il primo punto si parla, vorrei fare qualcosa di simile a quanto segue (che isn necessario per il secondo punto):

na.omit(iris[ , which(sapply(iris, class) == "numeric")]) 

Per separare il colonne bei sia numeric o character, uso il seguente:

iris[ , which(sapply(iris, class) == "numeric")] 
iris[ , which(sapply(iris, class) == "character")] 

2. Il secondo compito combino la riga precedente con colMeans:

colMeans(iris[ , which(sapply(iris, class) == "numeric")], na.rm = TRUE) 

3. Per estrarre il primo elemento delle colonne di caratteri, si può semplicemente fare:

iris[1, which(sapply(iris, class) == "character")] 

Nel caso citato per i dati dell'iride, la prima riga è completamente NA, anche le colonne di caratteri, quindi vorrei iterare per trovare il primo non-NA-fila

k <- 1 
while(any(is.na(FirstCharacterElement <- iris[k, which(sapply(iris, class) == "character")]))){ 
    k <- k + 1 
} 

Fate attenzione per il fattore di classe (che rompere il codice nel caso dei dati dell'iride dove la colonna specie è del fattore di classe e forse si aspettano di essere un personaggio colonna. Puoi verificarlo con sapply(iris, class) e modificarlo con, ad esempio,

iris$Species <- as.character(iris$Species) #or with similar column names 

Quando si leggono nei dati si può citare il parametro stringsAsFactors = FALSE di funzioni read.table, read.csv o simili.