2013-10-04 18 views
5

Ho una matrice di caratteri con struttura come la seguente:allineamento come righe di una matrice di caratteri in R

dog 1 cow 9  mouse 7 
bird 10 tiger 1  gnu 2 
tiger 3 deer 7  deer 27 
skunk 2 rat 50 NA  NA 
mouse 8 snake 3  NA  NA 
cow 7 NA  NA NA  NA 
sheep 21 NA  NA NA  NA 
gnu 5 NA  NA NA  NA 

Immaginate questa sia una matrice di animali in locali con i dati per ogni lingua definita da successive coppie di colonne. Alcuni animali possono essere comuni tra locali, ma le località possono anche avere animali unici. In definitiva voglio fare una mappa termica di questi dati, e quindi necessità di riordinare questa matrice di avere una struttura in cui v'è una colonna per tutti i tipi di animali e colonne successive corrispondenti ai numeri in ogni locale:

dog 1 NA NA 
tiger 3 1  NA 
skunk 2 NA NA 
mouse 8 NA NA 
cow 7 9  NA 
sheep 21 NA NA 
gnu 5 NA 2 
deer NA 7  27 
rat NA 50 NA 
snake NA 3  NA 
mouse NA NA 7 
bird 10 NA NA 

In altre parole, mi hanno

A1 <- c("dog", "bird", "tiger", "skunk", "mouse", "cow", "sheep", "gnu") 
B1 <- as.character(c(1, 10, 3, 2, 8, 7, 21, 5)) 
A2 <- c("cow", "tiger", "deer", "rat", "snake", NA, NA, NA) 
B2 <- as.character(c(9, 1, 7, 50, 3, NA, NA, NA)) 
A3 <- c("mouse", "gnu", "deer", NA, NA, NA, NA, NA) 
B3 <- as.character(c(7, 2, 27, NA, NA, NA, NA, NA)) 
TheMatrix <- cbind(A1, B1, A2, B2, A3, B3) 

e vogliono

a1 <- c("dog", "tiger", "skunk", "mouse", "cow", "sheep", "gnu", "deer", "rat", "snake", "mouse", "bird") 
b1 <- as.character(c(1, 3, 2, 8, 7, 21, 5, NA, NA, NA, NA, 10)) 
b2 <- as.character(c(NA, 1, NA, NA, 9, NA, NA, 7, 50, 3, NA, NA)) 
b3 <- as.character(c(NA, NA, NA, NA, NA, NA, 2, 27, NA, NA, 7, NA)) 
DesiredResult <- cbind(a1, b1, b2, b3) 

idee su come realizzare questa riorganizzazione? Potrebbe essere fatto con cicli e contabilità, ma sicuramente c'è un modo più elegante che mi manca.

risposta

5
library(reshape2) 

ncols = ncol(TheMatrix) 
nrows = nrow(TheMatrix) 

dcast(as.data.frame(na.omit(cbind(c(TheMatrix[,seq(1,ncols,2)]), 
            c(TheMatrix[,seq(2,ncols,2)]), 
            rep(colnames(TheMatrix)[seq(2,ncols,2)], 
             each = nrows)))), 
     V1 ~ V3, value.var = 'V2') 
#  V1 B1 B2 B3 
#1 bird 10 <NA> <NA> 
#2 cow 7 9 <NA> 
#3 deer <NA> 7 27 
#4 dog 1 <NA> <NA> 
#5 gnu 5 <NA> 2 
#6 mouse 8 <NA> 7 
#7 rat <NA> 50 <NA> 
#8 sheep 21 <NA> <NA> 
#9 skunk 2 <NA> <NA> 
#10 snake <NA> 3 <NA> 
#11 tiger 3 1 <NA> 

Ci sono un sacco di cose (che sono abbastanza semplici ciascuna) accade qui e di capire, basta eseguire ogni po 'da solo (a partire dalla parte interna e uscire).

+0

grazie per avermi insegnato qualcosa di nuovo su 'cbind'. –

+0

Grazie, non ho mai lavorato con reshape2, questo è il trucco! – user2535366

2

Ecco il mio prendere:

> x <- read.table(text = " 
+ dog 1 cow 9  mouse 7 
+ bird 10 tiger 1  gnu 2 
+ tiger 3 deer 7  deer 27 
+ skunk 2 rat 50 NA  NA 
+ mouse 8 snake 3  NA  NA 
+ cow 7 NA  NA NA  NA 
+ sheep 21 NA  NA NA  NA 
+ gnu 5 NA  NA NA  NA ") 

A. di trasformare i dati di origine in una lista di frame di dati con 3 colonne: animale, contare e il numero locale:

> ll <- lapply(1:(ncol(x)/2), 
       function(i) cbind(x[c(2*i-1, 2*i)], data.frame(locale = i))) 
[[1]] 
    V1 V2 locale 
1 dog 1  1 
2 bird 10  1 
3 tiger 3  1 
4 skunk 2  1 
5 mouse 8  1 
6 cow 7  1 
7 sheep 21  1 
8 gnu 5  1 

[[2]] 
    V3 V4 locale 
1 cow 9  2 
2 tiger 1  2 
3 deer 7  2 
4 rat 50  2 
5 snake 3  2 
6 <NA> NA  2 
7 <NA> NA  2 
8 <NA> NA  2 

[[3]] 
    V5 V6 locale 
1 mouse 7  3 
2 gnu 2  3 
3 deer 27  3 
4 <NA> NA  3 
5 <NA> NA  3 
6 <NA> NA  3 
7 <NA> NA  3 
8 <NA> NA  3 

B. rbind questi frame di dati insieme. Si dovrebbe fare i nomi di tutti i dati di strutture uguali prima, altrimenti non funzionerà rbind:

> for (i in 1:(ncol(x)/2)) names(ll[[i]])[1:2] <- c("animal", "count") 
> x <- Reduce(rbind, ll) 
    animal count locale 
1  dog  1  1 
2 bird 10  1 
3 tiger  3  1 
4 skunk  2  1 
5 mouse  8  1 
6  cow  7  1 
7 sheep 21  1 
8  gnu  5  1 
9  cow  9  2 
10 tiger  1  2 
11 deer  7  2 
12 rat 50  2 
13 snake  3  2 
14 <NA> NA  2 
15 <NA> NA  2 
16 <NA> NA  2 
17 mouse  7  3 
18 gnu  2  3 
19 deer 27  3 
20 <NA> NA  3 
21 <NA> NA  3 
22 <NA> NA  3 
23 <NA> NA  3 
24 <NA> NA  3 

C. Quindi, utilizzare dcast dal pacchetto reshape2:

> library(reshape2) 
> dcast(x, animal ~ locale, fun.aggregate = sum, value.var = "count") 
    animal 1 2 3 
1 bird 10 0 0 
2  cow 7 9 0 
3 deer 0 7 27 
4  dog 1 0 0 
5  gnu 5 0 2 
6 mouse 8 0 7 
7  rat 0 50 0 
8 sheep 21 0 0 
9 skunk 2 0 0 
10 snake 0 3 0 
11 tiger 3 1 0 
12 <NA> 0 NA NA 

D. Ultimo passo per ripulire l'output e sostituire 0 con NA è lasciato come esercizio per il lettore :).

+0

Grazie, i tuoi commenti sono utili. E ho rimosso gli ANs :) – user2535366

0

Qui una soluzione con Reduce

#provide number of locales 
max_locale=3 
#this list contains the column numbers we want to use to split TheMatrix 
split_list=split(1:(2*max_locale),sort(rep(1:max_locale,2))) 

#this function will be used to re-merge the split matrix 
my_locale_merge=function(x,y) { 
    merge(x,y,by.x=colnames(x)[1],by.y=colnames(y)[1],all=TRUE) 
} 

#the outer subset is used to get rid of the NA animals 
subset(
    #reduce subsequently applies my_locale_merge to the split matrix 
    Reduce(
     "my_locale_merge", 
     #lapply is used to split the matrix 
     lapply(split_list,function(x) { 
      as.data.frame(TheMatrix[,x,drop=FALSE],stringsAsFactors=FALSE) 
      }) 
     ), 
    !is.na(A1) 
) 

Per quanto comprendo, Reduce non consente all'utente di trasmettere argomenti delle funzioni aggiuntive come by.x. Pertanto, stavo definendo una nuova funzione my_locale_merge che gestisce questi argomenti.

Problemi correlati