如何仅使用非 NA 值为数据帧的每一行制作 bin?
How to make bin for the each row of dataframe only with the non NA values?
我有一个包含值和 NA 的数据框。其中一些在行的开头有 NA,一些在行的末尾有 NA。
# like this way
df<- data.frame(A=c(1,5,6, 1,NA,NA),
B=c(1,2,3, 2,NA,NA),
C=c(1,3,NA, 4,3,NA),
D=c(1,1,NA, 6,10,NA),
E=c(1,NA,NA, 1,1,1),
F=c(1,NA,NA, 1,1,1))
现在我想根据非 NA 值为每一行构建两个 bin 并将它们相加。
#expected output
Sum Bin
3 1
3 2
7 1
5 2
6 1
3 2
...
现在我所做的是首先根据该行是以 NA 开头还是以 NA 结尾将数据帧分成两部分。然后我用一个循环来计算。
df_bin <- data.frame(Sum = 0, Bin = 0)
bin = 2 # set bin for the calculation
for (i in 1:nrow(df)) {
l <- sum(!is.na(df[i,]))
ll <- as.integer(l/bin)
s <- c()
j <- 1
while (j <= (bin-1)) {
k <- sum(df[i,(j*ll-ll+1):(j*ll)])
s <- c(s,k)
j = j+1
}
k <- k <- sum(df[i,(j*(bin-1)+1):l])
s <- c(s,k)
df2 <- data.frame(Sum = s, Bin = 1:bin)
df_bin <- rbind(df_bin,df2)
}
但是运行起来很慢,我想知道是否有更优雅的方法来实现。提前谢谢你:)
您可以尝试使用 apply
:
do.call(rbind, apply(df, 1, function(x) {
#Remove NA values
x1 <- na.omit(x)
#Calculate length of non-NA values
n <- length(x1)
#Calculate mid point
half_len <- round(n/2)
#Create dataframe with sum of two bin values
data.frame(Sum = c(sum(x1[1:half_len]), sum(x1[(half_len + 1):n])),
Bin = 1:2)
}))
# Sum Bin
#1 3 1
#2 3 2
#3 7 1
#4 4 2
#5 6 1
#6 3 2
#7 7 1
#8 8 2
#9 13 1
#10 2 2
#11 1 1
#12 1 2
使用旋转的纯 tidyverse
解决方案:
df %>%
mutate(orig_row = 1:n()) %>%
pivot_longer(-orig_row) %>% filter(!is.na(value)) %>%
group_by(orig_row) %>% mutate(Bin = round(1 + seq(0, n() - 1) / n())) %>%
group_by(orig_row, Bin) %>% summarise(Sum = sum(value)) %>% ungroup() %>%
select(-orig_row)
结果:
# A tibble: 12 x 2
Bin Sum
<dbl> <dbl>
1 1 3
2 2 3
3 1 7
4 2 4
5 1 6
6 2 3
7 1 7
8 2 8
9 1 13
10 2 2
11 1 1
12 2 1
我有一个包含值和 NA 的数据框。其中一些在行的开头有 NA,一些在行的末尾有 NA。
# like this way
df<- data.frame(A=c(1,5,6, 1,NA,NA),
B=c(1,2,3, 2,NA,NA),
C=c(1,3,NA, 4,3,NA),
D=c(1,1,NA, 6,10,NA),
E=c(1,NA,NA, 1,1,1),
F=c(1,NA,NA, 1,1,1))
现在我想根据非 NA 值为每一行构建两个 bin 并将它们相加。
#expected output
Sum Bin
3 1
3 2
7 1
5 2
6 1
3 2
...
现在我所做的是首先根据该行是以 NA 开头还是以 NA 结尾将数据帧分成两部分。然后我用一个循环来计算。
df_bin <- data.frame(Sum = 0, Bin = 0)
bin = 2 # set bin for the calculation
for (i in 1:nrow(df)) {
l <- sum(!is.na(df[i,]))
ll <- as.integer(l/bin)
s <- c()
j <- 1
while (j <= (bin-1)) {
k <- sum(df[i,(j*ll-ll+1):(j*ll)])
s <- c(s,k)
j = j+1
}
k <- k <- sum(df[i,(j*(bin-1)+1):l])
s <- c(s,k)
df2 <- data.frame(Sum = s, Bin = 1:bin)
df_bin <- rbind(df_bin,df2)
}
但是运行起来很慢,我想知道是否有更优雅的方法来实现。提前谢谢你:)
您可以尝试使用 apply
:
do.call(rbind, apply(df, 1, function(x) {
#Remove NA values
x1 <- na.omit(x)
#Calculate length of non-NA values
n <- length(x1)
#Calculate mid point
half_len <- round(n/2)
#Create dataframe with sum of two bin values
data.frame(Sum = c(sum(x1[1:half_len]), sum(x1[(half_len + 1):n])),
Bin = 1:2)
}))
# Sum Bin
#1 3 1
#2 3 2
#3 7 1
#4 4 2
#5 6 1
#6 3 2
#7 7 1
#8 8 2
#9 13 1
#10 2 2
#11 1 1
#12 1 2
使用旋转的纯 tidyverse
解决方案:
df %>%
mutate(orig_row = 1:n()) %>%
pivot_longer(-orig_row) %>% filter(!is.na(value)) %>%
group_by(orig_row) %>% mutate(Bin = round(1 + seq(0, n() - 1) / n())) %>%
group_by(orig_row, Bin) %>% summarise(Sum = sum(value)) %>% ungroup() %>%
select(-orig_row)
结果:
# A tibble: 12 x 2
Bin Sum
<dbl> <dbl>
1 1 3
2 2 3
3 1 7
4 2 4
5 1 6
6 2 3
7 1 7
8 2 8
9 1 13
10 2 2
11 1 1
12 2 1