วนซ้ำตามคอลัมน์เพื่อรวมตัวเลข 2 ตัวก่อนหน้าของแต่ละแถว
ใน R ฉันมีดาต้าเฟรมที่มีคอลัมน์ 'A', 'B', 'C', 'D' คอลัมน์มี 100 แถว
ฉันต้องวนซ้ำในคอลัมน์เพื่อทำการคำนวณสำหรับแถวทั้งหมดในดาต้าเฟรมซึ่งรวม 2 แถวก่อนหน้าของคอลัมน์นั้นจากนั้นตั้งค่าในคอลัมน์ใหม่ ('AA', 'AB' ฯลฯ ) ผลรวมนั้นคืออะไร:
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
ถึง
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
ใครช่วยอธิบายวิธีสร้างฟังก์ชัน / ลูปที่ช่วยให้ฉันตั้งค่าคอลัมน์ที่ฉันต้องการทำซ้ำได้ (คอลัมน์ที่เลือกไม่ใช่คอลัมน์ทั้งหมด) และคอลัมน์ที่ฉันต้องการตั้งค่า
คำตอบ
base
หนึ่งซับ:
cbind(df, setNames(df + df[c(NA, 1:(nrow(df)-1)), ], paste0("A", names(df))))
data.frame
ถ้าข้อมูลของคุณมีขนาดใหญ่หนึ่งนี้อาจจะเร็วที่สุดเพราะมันปรุงแต่งทั้งหมด
dplyr
วิธีการแก้ปัญหาโดยใช้กับmutate()
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
หากคุณต้องการรวม 3 แถวก่อนหน้าอาร์กิวเมนต์ที่สองของacross()
เช่น.fns
ควรเป็น
~ .x + lag(.x) + lag(.x, 2)
ซึ่งเทียบเท่ากับการใช้rollsum()
ในzoo
:
~ zoo::rollsum(.x, k = 3, fill = NA, align = 'right')
เกณฑ์มาตรฐาน
การทดสอบเกณฑ์มาตรฐานพร้อมmicrobenchmark
แพ็กเกจใหม่ที่data.frame
มี 10,000 แถวและ 100 คอลัมน์และประเมินแต่ละนิพจน์ 10 ครั้ง
# 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
คุณสามารถใช้dplyr
's across
(และตั้งชื่อทางเลือก) กับผลรวมแบบกลิ้ง (ตามที่ใช้เช่นในzoo
):
library(dplyr)
library(zoo)
df %>%
mutate(
across(
A:D,
~ rollsum(., k = 2, fill = NA, align = 'right'),
.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
เมื่อA:D
เราได้ระบุช่วงของชื่อคอลัมน์ที่เราต้องการใช้ฟังก์ชันนี้ สมมติฐานข้างต้นใน.names
อาร์กิวเมนต์คือคุณต้องการวางเข้าด้วยกันA
เป็นคำนำหน้าและชื่อคอลัมน์ ( {col}
)
นี่คือโซลูชัน data.table ตามที่คุณขอคุณสามารถเลือกคอลัมน์ที่คุณต้องการใช้แทนคอลัมน์ทั้งหมดได้
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 นี่เร็วกว่าคำตอบอื่นที่เร็วที่สุด 2 หรือ 3 เท่า
นั่นเป็นวิธีการที่ไร้เดียงสาโดยมีfor
ลูปซ้อนกัน ระวังมันจะช้าถ้าคุณจะวนซ้ำมากกว่าแสนแถว
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
}
}
นั่นคือcumsum
วิธีการ:
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),]))
เกณฑ์มาตรฐาน:
# 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 เสียไปกับการจัดสรร df หากปรับให้เข้ากับแบ็กเอนด์ data.table อย่างถูกต้องอาจเร็วที่สุด
ระบุคอลัมน์ที่เราต้องการ เราแสดงวิธีต่างๆในการทำเช่นนั้น จากนั้นใช้rollsumr
เพื่อรับคอลัมน์ที่ต้องการตั้งชื่อคอลัมน์และ cbind DF
ด้วย
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)
การให้:
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
บันทึก
อินพุตในรูปแบบที่ทำซ้ำได้:
DF <- structure(list(A = 1:6, B = 2:7, C = 3:8, D = 4:9),
class = "data.frame", row.names = c(NA, -6L))