【问题标题】:Replace NA values with the average of before and after row values in R将 NA 值替换为 R 中前后行值的平均值
【发布时间】:2021-08-23 15:26:02
【问题描述】:

我想编写一个代码,用前后行值的平均值替换所有 NA 值。以下代码适用于 1 个单列。有没有什么想法可以在不将列名一一写入代码的情况下为数据集的所有列运行代码?

data$WTI[is.na(data$WTI)] <- rowMeans(cbind(data$WTI[which(is.na(data$WTI))-1], 
                                  data$WTI[which(is.na(data$WTI))+1]))

这是我的数据的样子:

> dput(head(data))
structure(list(Timestamp = structure(c(1629417600, 1629331200, 
1629244800, 1629158400, 1629072000, 1628812800), tzone = "UTC", class = c("POSIXct", 
"POSIXt")), USDTRY = c(8.4852, 8.4939, 8.4485, 8.4284, 8.453, 
8.5171), EURTRY = c(9.9325, 9.9311, 9.8916, 9.8746, 9.9618, 10.0539
), EURUSD = c(1.1696, 1.1674, 1.171, 1.1708, 1.1777, 1.1791), 
    BIST100 = c(1444.63, 1439.86, 1449.59, 1461.69, 1455.25, 
    1447.64), TR2YT = c(18.01, 18.01, 18.01, 18.01, 18.01, 18.15
    ), TR10YT = c(16.88, 16.87, 16.79, 16.8, 16.69, 16.77), TR_EURBON_2 = c(3.648673, 
    3.63085, 3.611969, 3.572728, 3.567871, 3.559959), TR_EURBON_10 = c(6.302608, 
    6.307343, 6.276473, 6.240502, 6.255035, 6.301358), BRENT = c(65.18, 
    66.45, 68.23, 69.03, 69.51, 70.59), WTI = c(62.32, 63.69, 
    65.46, 66.59, 67.29, 68.44), Altın = c(1780.8668, 1780.179, 
    1787.59, 1785.9556, 1787.2383, 1779.1515), Gümüş = c(23.01, 
    23.23, 23.4805, 23.6351, 23.8235, 23.74)), row.names = c(NA, 
-6L), class = c("tbl_df", "tbl", "data.frame"))

谢谢。

【问题讨论】:

  • 请与dput(head(data))分享您的数据集的可重复样本。
  • @SBA 请将dput() 编辑到您的原始问题中,不要将其输入到 cmets 中。

标签: r


【解决方案1】:

试试这个,

set.seed(42)
dat <- as.data.frame(matrix(sample(c(NA, 1:9), size = 35, replace = TRUE), ncol = 7))
dat
#   V1 V2 V3 V4 V5 V6 V7
# 1 NA  3  6  9  3  7  7
# 2  4  1  3  1  4  2  5
# 3 NA  9  8  2  4  9  9
# 4  8 NA  4  8  3 NA  7
# 5  9  7  3  8  1  9  3

dat[] <- lapply(dat, function(z) {
   mtx <- cbind(c(NA, head(z, -1)), z, c(tail(z, -1), NA))
   mtx[is.na(mtx[,2]) & rowSums(is.na(mtx)) > 1,] <- NA
   out <- ifelse(is.na(mtx[,2]), rowMeans(mtx, na.rm = TRUE), mtx[,2])
   out[is.nan(out)] <- NA
   out
 })
dat
#   V1 V2 V3 V4 V5 V6 V7
# 1 NA  3  6  9  3  7  7
# 2  4  1  3  1  4  2  5
# 3  6  9  8  2  4  9  9
# 4  8  8  4  8  3  9  7
# 5  9  7  3  8  1  9  3

如果您希望 V1[1] 在没有“之前”值的情况下也被更新,则删除 mtx[...] &lt;- NA 分配:

# fresh dat
dat[] <- lapply(dat, function(z) {
   mtx <- cbind(c(NA, head(z, -1)), z, c(tail(z, -1), NA))
   out <- ifelse(is.na(mtx[,2]), rowMeans(mtx, na.rm = TRUE), mtx[,2])
   out[is.nan(out)] <- NA
   out
 })
dat
#   V1 V2 V3 V4 V5 V6 V7
# 1  4  3  6  9  3  7  7
# 2  4  1  3  1  4  2  5
# 3  6  9  8  2  4  9  9
# 4  8  8  4  8  3  9  7
# 5  9  7  3  8  1  9  3

【讨论】:

    【解决方案2】:

    一种方法是使用leadlag

    library(dplyr)
    Timestamp %>%
            mutate(
                across(where(is.numeric), 
                    ~if_else(is.na(.), (dplyr::lead(.) + dplyr::lag(.)) / 2, .)
                    )
                )
    

    mutate 修改现有列,across 选择 is.numeric 返回 true 的列。 ~if_else(is.na(.), (dplyr::lead(.) + dplyr::lag(.)) / 2, .) 检查列中的当前值是否为NA,如果是,则将其替换为前后列的平均值。

    【讨论】:

    • 谢谢,但我认为使用您的编码,我将需要始终更改列名 (mpg) 来运行代码。我想为所有列编写代码,而不是一一写他们的名字。
    • 更新后的答案将适用于所有数字列。您可以通过更改across() 中的第一个参数来更改它应用于哪些列。
    • 感谢您的回答!
    猜你喜欢
    • 1970-01-01
    • 2023-03-13
    • 2014-08-12
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-25
    相关资源
    最近更新 更多