如何仅使用非 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