Her satırın önceki 2 sayısını toplamak için sütunları yineleyin

Aug 15 2020

R'de, 'A', 'B', 'C', 'D' sütunlarına sahip bir veri çerçevem ​​var. Sütunların 100 satırı vardır.

Veri çerçevesindeki tüm satırlar için o sütunun önceki 2 satırını toplayan bir hesaplama yapmak ve ardından yeni sütunlarda ('AA', 'AB', vb.) Bu toplamın ne olduğunu ayarlamak için sütunları yinelemem gerekiyor:

A  B  C  D 
1  2  3  4
2  3  4  5
3  4  5  6
4  5  6  7 
5  6  7  8
6  7  8  9

-e

A   B   C   D   AA   AB   AC   AD
1   2   3   4   NA   NA   NA   NA
2   3   4   5   3    5    7    9
3   4   5   6   5    7    9    11
4   5   6   7   7    9    11   13
5   6   7   8   9    11   13   15
6   7   8   9   11   13   15   17

Yinelemek istediğim sütunları (seçilen sütunlar, tüm sütunlar değil) ve ayarlamak istediğim sütunları ayarlamama izin veren bir işlevi / döngüyü nasıl oluşturacağımı birisi açıklayabilir mi?

Yanıtlar

2 DarrenTsai Aug 15 2020 at 17:50

Bir basetek astar:

cbind(df, setNames(df + df[c(NA, 1:(nrow(df)-1)), ], paste0("A", names(df))))

Verileriniz büyükse, bu en hızlı olabilir çünkü tamamını işliyor data.frame.


Bir dplyrkullanılarak çözelti mutate()ile across().

library(dplyr)

df %>%
  mutate(across(A:D,
                ~ .x + lag(.x),
                .names = "A{col}"))

#   A B C D AA AB AC AD
# 1 1 2 3 4 NA NA NA NA
# 2 2 3 4 5  3  5  7  9
# 3 3 4 5 6  5  7  9 11
# 4 4 5 6 7  7  9 11 13
# 5 5 6 7 8  9 11 13 15
# 6 6 7 8 9 11 13 15 17

Önceki 3 satırı toplamak istiyorsanız, ikinci argüman across(), ie .fns, olmalıdır

~ .x + lag(.x) + lag(.x, 2)

ki kullanımına eşdeğerdir rollsum()içinde zoo:

~ zoo::rollsum(.x, k = 3, fill = NA, align = 'right')

Kıyaslama

10000 satır ve 100 sütunlu microbenchmarkyeni bir paket ile bir kıyaslama testi data.frameve her bir ifadeyi 10 kez değerlendirin.

# Unit: milliseconds
#                     expr        min         lq       mean     median         uq        max neval
#              darren_base   18.58418   20.88498   35.51341   33.64953   39.31909   80.24725    10
#         darren_dplyr_lag   39.49278   40.27038   47.26449   42.89170   43.20267   76.72435    10
# arg0naut91_dplyr_rollsum  436.22503  482.03199  524.54800  516.81706  534.94317  677.64242    10
#    Grothendieck_rollsumr 3423.92097 3611.01573 3650.16656 3622.50895 3689.26404 4060.98054    10
1 arg0naut91 Aug 15 2020 at 17:49

dplyr'S' i kullanabilir across(ve isteğe bağlı adları ayarlayabilirsiniz) hareketli toplamla (uygulandığı gibi zoo):

library(dplyr)
library(zoo)

df %>%
  mutate(
    across(
      A:D,
      ~ rollsum(., k = 2, fill = NA, align = 'right'), 
      .names = 'A{col}'
    )
  )

Çıktı:

  A B C D AA AB AC AD
1 1 2 3 4 NA NA NA NA
2 2 3 4 5  3  5  7  9
3 3 4 5 6  5  7  9 11
4 4 5 6 7  7  9 11 13
5 5 6 7 8  9 11 13 15
6 6 7 8 9 11 13 15 17

İle A:Dişlevi uygulamak istediğimiz sütun adlarının aralığını belirledik. Yukarıdaki .namesargümandaki varsayım A, önek ve sütun adını ( {col}) birlikte yapıştırmak istediğinizdir .

1 pseudospin Aug 15 2020 at 19:34

İşte bir data.table çözümü. İstediğiniz gibi, yalnızca tüm sütunlar için değil, hangi sütunlara uygulamak istediğinizi seçmenize izin verir.

library(data.table)
x <- data.table(A=1:6, B=2:7, C=3:8, D=4:9)
selected_cols <- c('A','B','D')
new_cols <- paste0("A",selected_cols)
x[, (new_cols) := lapply(.SD, function(col) col+shift(col, 1)), .SDcols = selected_cols]
x[]

NB Bu, diğer en hızlı yanıttan 2 veya 3 kat daha hızlıdır.

1 ivan866 Aug 15 2020 at 17:59

Bu, iç içe geçmiş fordöngülerle saf bir yaklaşımdır . Yüz bin satırın üzerinde yineleme yapacaksanız çok yavaş olduğuna dikkat edin.

i <- 1
n <- 5
df <- data.frame(A=i:(i+n), B=(i+1):(i+n+1), C=(i+2):(i+n+2), D=(i+3):(i+n+3))
for (col in colnames(df)) {
  for (ind in 1:nrow(df)) {
    if (ind-1==0) {next}
    s <- sum(df[c(ind-1, ind), col])
    df[ind, paste0('S', col)] <- s
  }
}  

Bu bir cumsumyöntemdir:

na.df <- data.frame(matrix(NA, 2, ncol(df)))
colnames(na.df) <- colnames(df)
cs1 <- cumsum(df)
cs2 <- rbind(cs1[-1:-2,], na.df)
sum.diff <- cs2-cs1
cbind(df, rbind(na.df[1,], cs1[2,], sum.diff[1:(nrow(sum.diff)-2),]))  

Kıyaslama:

#    Unit: milliseconds  
#                      expr     min       lq     mean   median       uq     max neval  
#          darrentsai.rbind 11.5623 12.28025 23.38038 16.78240 20.83420 91.9135   100  
#     darrentsai.rbind.rev1  8.8267  9.10945 15.63652  9.54215 14.25090 62.6949   100  
#             pseudopsin.dt  7.2696  7.52080 20.26473 12.61465 17.61465 69.0110   100  
#            ivan866.cumsum 25.3706 30.98860 43.11623 33.78775 37.36950 91.6032   100  

Cumsum yönteminin çoğu zaman df tahsislerinde boşa gittiğine inanıyorum. Data.table arka uca doğru şekilde uyarlanırsa, en hızlısı olabilir.

1 G.Grothendieck Aug 16 2020 at 03:14

İstediğimiz sütunları belirtin. Bunu yapmanın birkaç farklı yolunu gösteriyoruz. Ardından rollsumr, istenen sütunları elde etmek, sütun adlarını ayarlamak ve DFonunla cbind yapmak için kullanın.

library(zoo)

# jx <- names(DF)  # if all columns wanted
# jx <- sapply(DF, is.numeric)  # if all numeric columns
# jx <- c("A", "B", "C", "D")  # specify columns by name
jx <- 1:4   # specify columns by position

r <- rollsumr(DF[jx], 2, fill = NA)
colnames(r) <- paste0("A", colnames(r))
cbind(DF, r)

veren:

  A B C D AA AB AC AD
1 1 2 3 4 NA NA NA NA
2 2 3 4 5  3  5  7  9
3 3 4 5 6  5  7  9 11
4 4 5 6 7  7  9 11 13
5 5 6 7 8  9 11 13 15
6 6 7 8 9 11 13 15 17

Not

Tekrarlanabilir formdaki girdi:

DF <- structure(list(A = 1:6, B = 2:7, C = 3:8, D = 4:9), 
  class = "data.frame", row.names = c(NA, -6L))