2016-12-19 16 views
5

mam matrycę jak tenScalanie kolumny o dataframe autorstwa dwóch warunków wykorzystaniem kruszywa

P A B C 
    1 2 0 5 
    2 1 1 3 
    3 0 4 7 
    1 1 1 0 
    3 1 1 0 
    3 0 2 1 
    2 3 3 4 

I scalić/sortować wiersze przez P i przez każdą z kolumn. Tak, że każda wartość P jest dla każdej kolumny raz, a wartość dla każdego P w każdej kolumnie jest sumowana. Wynik powinien być:

P A B C 
1 3 0 0 
1 0 1 0 
1 0 0 5 
2 4 0 0 
2 0 4 0 
2 0 0 7 
3 1 0 0 
3 0 7 0 
3 0 0 8 

już aggregate próbował ale tylko pomaga mi podsumować każdą wartość P dla wszystkich kolumnach tak, że mam tylko jeden wiersz dla każdego P.

+0

ponieważ każdy wiersz p pojawi się dla każdej kolumny. – Miguel123

+0

to jest coś jak mieszanka scalania i podziału – Miguel123

Odpowiedz

3

Innym pomysłem jest użycie samego diag funkcji do tworzenia matrycy. Następnie możesz połączyć te macierze ze sobą.

xx=aggregate(. ~ P, df, sum) 
yy=xx[,-1] 
yy=as.data.frame(t(yy)) 
cbind(rep(1:ncol(yy),nrow(yy)),do.call("rbind", lapply(yy, function(xx) diag(xx, nrow = nrow(yy), ncol = nrow(yy))))) 

     [,1] [,2] [,3] [,4] 
[1,] 1 3 0 0 
[2,] 2 0 1 0 
[3,] 3 0 0 5 
[4,] 1 4 0 0 
[5,] 2 0 4 0 
[6,] 3 0 0 7 
[7,] 1 1 0 0 
[8,] 2 0 7 0 
[9,] 3 0 0 8 
+0

Możesz chcieć go trochę uogólnić, aby uwzględnić więcej kolumn (np. A, B, C, D, E ...) – Sotos

+0

tak, moja oryginalna macierz to 95x20 – Miguel123

+0

To powinno działać dla dowolnego wymiaru. –

3

Otrzymujemy wartość maksymalną częstotliwości z kolumny "P" ("i1"), aggregate kolumny pogrupowane według "P", aby uzyskać sum ("df2"), powtórzyć wiersze "df2" przez "i1", split zbiór danych przez " P 'i zmień elementy inne niż diagonalne w innych kolumnach na 0 i zwróć je jako data.frame, order i zmień nazwy wierszy na NULL.

i1 <- max(table(df1$P)) 
df2 <- aggregate(.~P, df1, sum) 
df3 <- df2[rep(1:nrow(df2), i1)] 
res <- unsplit(lapply(split(df3, df3$P), function(x) { 
     x[-1] <- diag(3)*x[-1] 
     x}), df3$P) 
res1 <- res[order(res$P),] 
row.names(res1) <- NULL 
res1 
# P A B C 
#1 1 3 0 0 
#2 1 0 1 0 
#3 1 0 0 5 
#4 2 4 0 0 
#5 2 0 4 0 
#6 2 0 0 7 
#7 3 1 0 0 
#8 3 0 7 0 
#9 3 0 0 8 

lub używając data.table, konwertować 'data.frame' do 'data.table' (setDT(df1)), pętla przez podzbiór Data.table (.SD), uzyskać sum, pogrupowane według ' P ', replikuj wiersze podsumowanego zbioru danych i zmieniaj elementy niediagonalne na 0 (jak omówiono w pierwszym rozwiązaniu).

library(data.table) 
setDT(df1)[, lapply(.SD, sum), by = P 
      ][rep(1:.N, i1) 
      ][, .SD*diag(ncol(df1)-1), by = P] 
# P A B C 
#1: 1 3 0 0 
#2: 1 0 1 0 
#3: 1 0 0 5 
#4: 2 4 0 0 
#5: 2 0 4 0 
#6: 2 0 0 7 
#7: 3 1 0 0 
#8: 3 0 7 0 
#9: 3 0 0 8 

lub używając dplyr

library(dplyr) 
library(purrr) 
d1 <- as.data.frame(diag(i1)) 
df2 <- df1 %>% 
      group_by(P) %>% 
      summarise_each(funs(sum)) %>% 
      replicate(i1, ., simplify = FALSE) %>% 
      bind_rows() %>% 
      arrange(P) 
df2[-1] <- map2(df2[-1], d1, ~.x * .y) 
df2 
# A tibble: 9 × 4 
#  P  A  B  C 
# <int> <dbl> <dbl> <dbl> 
#1  1  3  0  0 
#2  1  0  1  0 
#3  1  0  0  5 
#4  2  4  0  0 
#5  2  0  4  0 
#6  2  0  0  7 
#7  3  1  0  0 
#8  3  0  7  0 
#9  3  0  0  8 
+1

jakoś twoje drugie rozwiązanie jest jedyne, które działa! Wielkie dzięki - byłem tak naprawiony przy użyciu agregatu – Miguel123

+0

dobrze ... przepraszam, właśnie odkryłem, że z moimi ogromnymi danymi to nie działa. hmm – Miguel123

4

Jednym z pomysłów jest, aby podzielić ramkę danych na P i zastosować funkcję niestandardową (fun1) tworzy macierz z 0 i zastępuje przekątnej z suma kolumn. tj

fun1 <- function(x){ 
m1 <- matrix(0, ncol = ncol(x) - 1, nrow = ncol(x) - 1) 
diag(m1) <- sapply(x[-1], sum) 
return(m1) 
     } 

l1 <- split(df, df$P) 
do.call(rbind, lapply(l1, fun1)) 

#  [,1] [,2] [,3] 
# [1,] 3 0 0 
# [2,] 0 1 0 
# [3,] 0 0 5 
# [4,] 4 0 0 
# [5,] 0 4 0 
# [6,] 0 0 7 
# [7,] 1 0 0 
# [8,] 0 7 0 
# [9,] 0 0 8 

albo zmusić go do żądanego wyjścia, a następnie

final_df <- as.data.frame(cbind(rep(names(l1), each = ncol(df)-1), 
              do.call(rbind, lapply(l1, fun1)))) 
names(final_df) <- names(df) 

final_df 
# P A B C 
#1 1 3 0 0 
#2 1 0 1 0 
#3 1 0 0 5 
#4 2 4 0 0 
#5 2 0 4 0 
#6 2 0 0 7 
#7 3 1 0 0 
#8 3 0 7 0 
#9 3 0 0 8 
+0

to fajny pomysł, ale jakoś nie mogę wygenerować funkcji ... – Miguel123

+0

Spróbuj ponownie ... edytował funkcję – Sotos

+0

hmm wydaje się, że coś jest nie tak z długością przekątnej – Miguel123

1

O ile czegoś nie brakuje, poniższe wygląda również. Rozpoczęcie przez obliczenie sumy na "P":

s = as.matrix(rowsum(dat[-1], dat$P)) 

Tworzenie końcowej matrycy:

k = s[rep(1:nrow(s), each = ncol(s)), ] 

indeksy Oblicz wymienić na "0" S:

k[col(k) != (row(k) - 1) %% ncol(k) + 1] = 0 
k 
# A B C 
#1 3 0 0 
#1 0 1 0 
#1 0 0 5 
#2 4 0 0 
#2 0 4 0 
#2 0 0 7 
#3 1 0 0 
#3 0 7 0 
#3 0 0 8 

danych:

dat = structure(list(P = c(1L, 2L, 3L, 1L, 3L, 3L, 2L), A = c(2L, 1L, 
0L, 1L, 1L, 0L, 3L), B = c(0L, 1L, 4L, 1L, 1L, 2L, 3L), C = c(5L, 
3L, 7L, 0L, 0L, 1L, 4L)), .Names = c("P", "A", "B", "C"), class = "data.frame", row.names = c(NA, 
-7L)) 

Hav ING obliczane s, bardziej prostą alternatywę user20650 Add

matrix(diag(ncol(s)), nrow(s) * ncol(s), ncol(s), byrow = TRUE) * c(t(s)) 

lub też, brudząc z innych interesujących alternatyw na tej samej idei:

kronecker(rep_len(1, nrow(s)), diag(ncol(s))) * c(t(s)) 

diag(ncol(s))[rep(1:ncol(s), nrow(s)), ] * s[rep(1:nrow(s), each = ncol(s)), ] 
+0

bardzo ładnie. (Ive just hardcoded no.'s here), ale być może mały ale jaśniejszy ??? 't (macierz (diag (3), nrow = 3, ncol = 9)) * c (t (s))' – user20650

+0

@ user20650: recykling 'diag()' jest rzeczywiście przydatny tutaj - nie myślał tego. Będę musiał potraktować mój recykling bardziej poważnie ...... teraz wydaje mi się, że parametryzacja jest poprawna. –

+0

miła Alexis, twoja metoda kroneckera jest całkiem sprytna. Myślę, że te transformacje macierzy są najlepszym sposobem podejścia do tego problemu ... wciąż ... – user20650