2013-04-25 12 views
18

Oto prosty przykład mój problem:R jak mogę obliczyć różnicę między rzędami w ramce danych

> df <- data.frame(ID=1:10,Score=4*10:1) 
> df 
     ID Score 
    1 1 40 
    2 2 36 
    3 3 32 
    4 4 28 
    5 5 24 
    6 6 20 
    7 7 16 
    8 8 12 
    9 9  8 
    10 10  4 
    > diff(df) 

Error in r[i1] - r[-length(r):-(length(r) - lag + 1L)] : 
    non-numeric argument to binary operator 

Czy ktoś może mi powiedzieć, dlaczego ten błąd występuje?

Odpowiedz

19

Może szukasz czegoś takiego:

> tail(df, -1) - head(df, -1) 
    ID Score 
2 1 -4 
3 1 -4 
4 1 -4 
5 1 -4 
6 1 -4 
7 1 -4 
8 1 -4 
9 1 -4 
10 1 -4 

Można dodać lub odjąć dwa data.frame y razem, jeśli są one takie same wymiary. To, co tutaj robimy, polega na odjęciu jednego data.frame, który nie ma pierwszego wiersza (tail(df, -1)) i brakuje tego ostatniego wiersza (head(df, -1)) i odjęcie ich.

+1

+1 Dobra sztuczka z główkami i ogonami. –

+0

+1 pomysłowy. Nigdy bym nie pomyślał, że 'head' z -1 zwróci wszystko oprócz pierwszego rzędu. Clever –

25

diff chce raczej macierzy lub wektora niż ramki danych. Wypróbuj

data.frame(diff(as.matrix(df))) 
+0

+1 nice - Przypuszczam, że ma to sens, ponieważ matryca jest po prostu wektorem z drugim atrybutem dim. –

+0

+1. To będzie zdecydowanie najszybsze rozwiązanie. – A5C1D2H2I1M1N2O1R2T1

+1

Dla odniesienia, niektóre [wzorce] (http://rpubs.com/mrdwab/bmft) na wierszu 1000000 przez 5 kolumna dataset. – A5C1D2H2I1M1N2O1R2T1

8

Ponieważ df działa na wektorze lub macierzy. Można użyć Zastosuj, aby zastosować funkcję w kolumnach tak:

apply(df , 2 , diff) 
    ID Score 
2 1 -4 
3 1 -4 
4 1 -4 
5 1 -4 
6 1 -4 
7 1 -4 
8 1 -4 
9 1 -4 
10 1 -4 

Wydaje się mało prawdopodobne, że chcesz obliczyć różnicę w kolejnych identyfikatorów, więc można wybrać, aby stosować go na wszystkich kolumnach wyjątkiem, pierwsza jak tak:

apply(df[-1] , 2 , diff) 

Albo można użyć data.table (nie to, że dodaje nic tu po prostu naprawdę chcesz zacząć go używać!), a ja znowu zakładając, że nie chcesz, aby zastosować diff do kolumny ID:

DT <- data.table(df) 
DT[ , list(ID,Score,Diff=diff(Score)) ] 
    ID Score Diff 
1: 1 40 -4 
2: 2 36 -4 
3: 3 32 -4 
4: 4 28 -4 
5: 5 24 -4 
6: 6 20 -4 
7: 7 16 -4 
8: 8 12 -4 
9: 9  8 -4 
10: 10  4 -4 

A dzięki @AnandaMahto alternatywnej składni, która daje większą swobodę wyboru, które kolumny, aby go uruchomić na mogłoby być:

DT[, lapply(.SD, diff), .SDcols = 1:2] 

Tutaj .SDcols = 1:2 oznacza chcesz zastosować funkcję diff do kolumn 1 i 2 Jeśli masz 20 kolumn i nie chcesz zastosować go do identyfikatora, możesz posłużyć się przykładem .SDcols=2:20.

+0

+1 dla edycji. Sprawi to, że będzie mniej dezorientujący dla przyszłych użytkowników tego pytania. – A5C1D2H2I1M1N2O1R2T1

3

Chciałbym pokazać alternatywny sposób robienia tego rodzaju rzeczy, nawet często mam wrażenie, że nie jest to doceniane w ten sposób: używając sql.

sqldf(paste("SELECT a.ID,a.Score" 
      ,"  , a.Score - (SELECT b.Score" 
      ,"     FROM df b" 
      ,"     WHERE b.ID < a.ID" 
      ,"     ORDER BY b.ID DESC" 
      ,"     ) diff" 
      ," FROM df a" 
      ) 
    ) 

Kod wydaje się skomplikowane, ale nie jest i ma pewną przewagę, jak widać w wynikach:

ID Score diff 
1 1 40 <NA> 
2 2 36 -4.0 
3 3 32 -4.0 
4 4 28 -4.0 
5 5 24 -4.0 
6 6 20 -4.0 
7 7 16 -4.0 
8 8 12 -4.0 
9 9  8 -4.0 
10 10  4 -4.0 

Jedną z zalet jest to, że używasz oryginalnego dataframe (bez konwersji do innego klasy) i otrzymujesz ramkę danych (umieść ją w res < - ....). Kolejną zaletą jest to, że wciąż masz wszystkie wiersze. Trzecią zaletą jest to, że można łatwo rozważyć czynniki grupujące.Na przykład:

df2 <- data.frame(ID=1:10,grp=rep(c("v","w"), each=5),Score=4*10:1) 

sqldf(paste("SELECT a.ID,a.grp,a.Score" 
      ,"  , a.Score - (SELECT b.Score" 
      ,"     FROM df2 b" 
      ,"     WHERE b.ID < a.ID" 
      ,"       AND a.grp = b.grp" 
      ,"     ORDER BY b.ID DESC" 
      ,"     ) diff" 
    ," FROM df2 a" 
    ) 
) 


    ID grp Score diff 
1 1 v 40 <NA> 
2 2 v 36 -4.0 
3 3 v 32 -4.0 
4 4 v 28 -4.0 
5 5 v 24 -4.0 
6 6 w 20 <NA> 
7 7 w 16 -4.0 
8 8 w 12 -4.0 
9 9 w  8 -4.0 
10 10 w  4 -4.0 
4

Dodawanie to kilka lat później za Całkowitość można użyć prostego [.data.frame subseting w celu osiągnięcia tego celu zbyt

df[-1, ] - df[-nrow(df), ] 
# ID Score 
# 2 1 -4 
# 3 1 -4 
# 4 1 -4 
# 5 1 -4 
# 6 1 -4 
# 7 1 -4 
# 8 1 -4 
# 9 1 -4 
# 10 1 -4 
+0

Już opublikowałeś. Więc możesz dodać te – akrun

+0

@akrun, masz tam kilka fajnych rzeczy. Dplyr sprawi, że na dłuższą metę zabiorą cię na górę. Opublikuj –

+0

OK, chociaż jest to sprzeczne z moimi zasadami :-) – akrun

4

Inną opcją korzystania dplyr byłoby korzystania mutate_each pętli wszystkie kolumny, uzyskaj różnicę w kolumnie (.) z lag kolumny (.) i usuń element NA z góry z na.omit()

library(dplyr) 
df %>% 
    mutate_each(funs(. - lag(.))) %>% 
    na.omit() 

Albo z shift od data.table. Konwersja 'data.frame' do 'data.table' (setDT(df)), pętla przez kolumny (lapply(.SD, ..) ) and get the difference between the column ( x ) and the lag ( przesunięcie by default gives the lag as type = "lag" `). Usuń pierwszą obserwację, tj. Element NA.

library(data.table) 
setDT(df)[, lapply(.SD, function(x) (x- shift(x))[-1])] 
Powiązane problemy