【问题标题】:Calculate column median absolute deviation (MAD) by column for each dataframe in a list of dataframes为数据帧列表中的每个数据帧按列计算列中值绝对偏差 (MAD)
【发布时间】:2021-07-15 14:22:59
【问题描述】:

我想通过忽略数据帧列表中每个数据帧的第一列的列来计算中值绝对偏差 (mscore)。然后将结果作为新行添加到行名为 mscore 的数据框中。

以前我会一次一个地对每个数据帧进行计算,但现在它简化了流程。

下面是我的数据框列表的一小部分。 dfs 的完整列表有超过 30 个数据帧


   list(Al2O3 = structure(list(Determination_No = 1:6, `2` = c(2.01, 
    2.02, 2.03, 2.01, 2.02, 2), `3` = c(2.01, 2.01, 2, 2.02, 2.02, 
    2.03), `4` = c(2, 2.03, 1.99, 2.01, 2.01, 2.01), `5` = c(2.02, 
    2.02, 2.05, 2.03, 2.02, 2.03), `7` = c(1.88, 1.9, 1.89, 1.88, 
    1.88, 1.87), `8` = c(2.053, 2.044, 2.041, 2.038, 2.008, 2.02), 
    `10` = c(2.002830415, 2.021725042, 2.021725042, 1.983935789, 
    2.002830415, 2.021725042), `12` = c(2.09, 2.05, 1.96, 2.09, 
    2.06, 2.02)), class = "data.frame", row.names = c(NA, -6L
    )), As = structure(list(Determination_No = 1:6, `2` = c(0.052, 
    0.027, 0.011, 0.011, 0.012, 0.012), `3` = c(0.012, 0.012, 0.013, 
    0.012, 0.013, 0.013), `4` = c(0.012, 0.012, 0.013, 0.012, 0.012, 
    0.012), `5` = c(0.013, 0.013, 0.013, 0.013, 0.013, 0.013), `7` = c(0.011, 
    0.011, 0.011, 0.012, 0.011, 0.011), `8` = c(0.011, 0.01, 0.011, 
    0.011, 0.011, 0.011), `10` = c(0.01, 0.01, 0.01, 0.01, 0.01, 
    0.01), `12` = c(NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, 
    NA_real_)), class = "data.frame", row.names = c(NA, -6L)), Fe = structure(list(
    Determination_No = 1:6, `2` = c(55.94, 55.7, 56.59, 56.5, 
    55.98, 55.93), `3` = c(56.83, 56.54, 56.18, 56.5, 56.51, 
    56.34), `4` = c(56.39, 56.43, 56.53, 56.31, 56.47, 56.35), 
    `5` = c(56.32, 56.29, 56.31, 56.32, 56.39, 56.32), `7` = c(56.48, 
    56.4, 56.54, 56.43, 56.73, 56.62), `8` = c(56.382, 56.258, 
    56.442, 56.258, 56.532, 56.264), `10` = c(56.3, 56.5, 56.2, 
    56.5, 56.7, 56.5), `12` = c(56.11, 56.46, 56.1, 56.35, 56.36, 
    56.37)), class = "data.frame", row.names = c(NA, -6L)))

以前我会做以下事情

#create a modified scores function to accept NAs

scores_na <- function(x, ...) { 
  not_na <- !is.na(x)
  scores <- rep(NA, length(x))
  scores[not_na] <- outliers::scores(na.omit(x), ...)
  scores
}

MscoreMax <- 3.0 # the the threshold to remove values deemed to be an outlier

 colmedians <- median, df[-1], na.rm = T)
 MScore <- as.vector(round(abs(scores_na(colmedians, "mad")), digits = 2)) #Mscore to 2 decimals 
places
 MscoreIndex <- which(MScore > MscoreMax) #get the index of each value exceeding the threshold
 df[-1][Fe.MscoreIndex] <- NA # change outliers to NA so they are excluded from further calculations

我已经尝试了下面的行来计算中位数

colmedians 函数用于矩阵,所以我使用 mapply 跨列应用

df <- lapply(df, function(x) rbind(x[,-1], 
                                   mapply(median(x[,-1],na.rm = TRUE))))

但是我得到了跟随错误

    Error in median.default(x[, -1], na.rm = TRUE) : need numeric data  

当我查询数据帧时,我的值被存储为 double 所以有点卡住了。

【问题讨论】:

  • 仅供参考,代码块由代码围栏分隔,代码围栏是三个 反引号 (```),而不是这里的单引号;见stackoverflow.com/editing-help
  • @r2evans 道歉
  • 无需道歉!我只是在问题格式方面提供建议。如果它太咄咄逼人,我很抱歉,我可以回滚。
  • @r2evans 不,你不是。反馈和编辑很有用。我正在寻求帮助,所以对每个人来说越容易越好
  • mapply 的使用在这里是错误的,但这很容易解决(第一个参数需要是一个函数,而不是函数调用的结果,但我不相信你需要mapplylapply 应该可以工作)。但是...绑定一行并添加行名是一回事,但行名很容易丢失。我通常不喜欢将汇总统计数据作为 row 添加到实际数据中,是否只是为了在报告中呈现/呈现而绑定该行?

标签: r dataframe lapply


【解决方案1】:

到目前为止,将函数应用于data.frame 中的所有或部分列的规范方法是lapply

我不认为mapply 在您那里使用时有用:它的第一个参数必须是function,未计算,而不是median(.) 中的计算表达式。不过,我认为我们可以改用lapply

仅供参考,这两个调用是mapplylapply 之间的等价物:

lapply(list(1:2, 3:4), sum)
mapply(sum, list(1:2, 3:4))

但是,只有mapply 可以直接执行此操作(lapply 可以使用索引而不是原始数据执行此操作):

mapply(function(x, y) sum(x) + y, list(1:2, 3:4), list(5, 6))

哪个“展开”到

sum(1:2) + 5
sum(3:4) + 6

我的第一步是汇总数据,这非常简单。

tmp <- lapply(z, function(x) as.data.frame(lapply(x[,-1], median, na.rm=TRUE), check.names = FALSE))
tmp
# $Al2O3
#       2     3    4     5    7      8       10    12
# 1 2.015 2.015 2.01 2.025 1.88 2.0395 2.012278 2.055
# $As
#       2      3     4     5     7     8   10 12
# 1 0.012 0.0125 0.012 0.013 0.011 0.011 0.01 NA
# $Fe
#       2      3     4     5     7      8   10     12
# 1 55.96 56.505 56.41 56.32 56.51 56.323 56.5 56.355

按照建议,我可以轻松地将此数据附加到原始数据并添加行名,但我想对此进行简要讨论:

  • 如果您要对数据进行更多的分析/计算,我认为在数据本身中添加汇总统计并不是一件好事;
  • 没有关于如何处理Determination_No 的明确指导;
  • 行名可能易碎;虽然许多 R 函数可以很好地使用和保存它们,但有些函数却不能……并且大多数来自 dplyr 和相关包(如果您使用它们)的函数都没有努力保存它们(偶尔会故意擦除它们);

最终,我怀疑您希望将其添加为最后一行是为了演示,所以最好(在我的脑海中)作为渲染过程的一部分完成。例如,

Map(function(nm, dat, smry) {
  dat$Determination_No <- as.character(dat$Determination_No)
  rbind(dat, cbind(data.frame(Determination_No = paste(nm, "median -->")), smry))
}, names(z), z, tmp)
# $Al2O3
#   Determination_No     2     3    4     5    7      8       10    12
# 1                1 2.010 2.010 2.00 2.020 1.88 2.0530 2.002830 2.090
# 2                2 2.020 2.010 2.03 2.020 1.90 2.0440 2.021725 2.050
# 3                3 2.030 2.000 1.99 2.050 1.89 2.0410 2.021725 1.960
# 4                4 2.010 2.020 2.01 2.030 1.88 2.0380 1.983936 2.090
# 5                5 2.020 2.020 2.01 2.020 1.88 2.0080 2.002830 2.060
# 6                6 2.000 2.030 2.01 2.030 1.87 2.0200 2.021725 2.020
# 7 Al2O3 median --> 2.015 2.015 2.01 2.025 1.88 2.0395 2.012278 2.055
# $As
#   Determination_No     2      3     4     5     7     8   10 12
# 1                1 0.052 0.0120 0.012 0.013 0.011 0.011 0.01 NA
# 2                2 0.027 0.0120 0.012 0.013 0.011 0.010 0.01 NA
# 3                3 0.011 0.0130 0.013 0.013 0.011 0.011 0.01 NA
# 4                4 0.011 0.0120 0.012 0.013 0.012 0.011 0.01 NA
# 5                5 0.012 0.0130 0.012 0.013 0.011 0.011 0.01 NA
# 6                6 0.012 0.0130 0.012 0.013 0.011 0.011 0.01 NA
# 7    As median --> 0.012 0.0125 0.012 0.013 0.011 0.011 0.01 NA
# $Fe
#   Determination_No     2      3     4     5     7      8   10     12
# 1                1 55.94 56.830 56.39 56.32 56.48 56.382 56.3 56.110
# 2                2 55.70 56.540 56.43 56.29 56.40 56.258 56.5 56.460
# 3                3 56.59 56.180 56.53 56.31 56.54 56.442 56.2 56.100
# 4                4 56.50 56.500 56.31 56.32 56.43 56.258 56.5 56.350
# 5                5 55.98 56.510 56.47 56.39 56.73 56.532 56.7 56.360
# 6                6 55.93 56.340 56.35 56.32 56.62 56.264 56.5 56.370
# 7    Fe median --> 55.96 56.505 56.41 56.32 56.51 56.323 56.5 56.355

使用knitr::kablegt 包、DT(如果在 html 环境中使用它)等表格渲染工具可以完成更多工作。

【讨论】:

    【解决方案2】:

    试试下面的

    lapply(A, function(x) {x[nrow(x) + 1,] <- apply(x, 2, function(y) median(y, na.rm = TRUE)); 
                      x <- x[,-1]; 
                      rownames(x)[nrow(x)] <- "mscores"; 
                              return(x)})
    

    然后我们得到结果

        > lapply(A, function(x) {x[nrow(x) + 1,] <- apply(x, 2, function(y) median(y, na.rm = TRUE)); x <- x[,-1]; rownames(x)[nrow(x)] <- "mscores"; return(x)})
    $Al2O3
                2     3    4     5    7      8       10    12
    1       2.010 2.010 2.00 2.020 1.88 2.0530 2.002830 2.090
    2       2.020 2.010 2.03 2.020 1.90 2.0440 2.021725 2.050
    3       2.030 2.000 1.99 2.050 1.89 2.0410 2.021725 1.960
    4       2.010 2.020 2.01 2.030 1.88 2.0380 1.983936 2.090
    5       2.020 2.020 2.01 2.020 1.88 2.0080 2.002830 2.060
    6       2.000 2.030 2.01 2.030 1.87 2.0200 2.021725 2.020
    mscores 2.015 2.015 2.01 2.025 1.88 2.0395 2.012278 2.055
    
    $As
                2      3     4     5     7     8   10 12
    1       0.052 0.0120 0.012 0.013 0.011 0.011 0.01 NA
    2       0.027 0.0120 0.012 0.013 0.011 0.010 0.01 NA
    3       0.011 0.0130 0.013 0.013 0.011 0.011 0.01 NA
    4       0.011 0.0120 0.012 0.013 0.012 0.011 0.01 NA
    5       0.012 0.0130 0.012 0.013 0.011 0.011 0.01 NA
    6       0.012 0.0130 0.012 0.013 0.011 0.011 0.01 NA
    mscores 0.012 0.0125 0.012 0.013 0.011 0.011 0.01 NA
    
    $Fe
                2      3     4     5     7      8   10     12
    1       55.94 56.830 56.39 56.32 56.48 56.382 56.3 56.110
    2       55.70 56.540 56.43 56.29 56.40 56.258 56.5 56.460
    3       56.59 56.180 56.53 56.31 56.54 56.442 56.2 56.100
    4       56.50 56.500 56.31 56.32 56.43 56.258 56.5 56.350
    5       55.98 56.510 56.47 56.39 56.73 56.532 56.7 56.360
    6       55.93 56.340 56.35 56.32 56.62 56.264 56.5 56.370
    mscores 55.96 56.505 56.41 56.32 56.51 56.323 56.5 56.355
    

    【讨论】:

    • 谢谢。您的解决方案计算中位数并允许我分配行名,这是一个很大的帮助
    • 不客气。如果它解决了您的问题,您可以通过投票和接受答案来表明这一点。
    • 不幸的是,这只是我需要的答案的一半
    • 除了每个 data.frame 的中位数和删除第一列之外,您还需要什么?您可以更新您最初的问题以反映这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-14
    • 2021-09-22
    • 1970-01-01
    相关资源
    最近更新 更多