在R中顺序计算列的中位数并将值存储在数据框中

Posted

技术标签:

【中文标题】在R中顺序计算列的中位数并将值存储在数据框中【英文标题】:Calculating median of a column sequentially in R and storing values in dataframe 【发布时间】:2022-01-09 21:02:48 【问题描述】:

我在计算列中值的中位数时遇到了问题。 我在一列中有数百个值,但我想一次计算一个序列中五个值的中值,并将中值存储在 R 中的单独数据框中,并类似地继续直到可用数据值结束。

问题是 #VALUE! 有一些值,并且在计算中位数时,如果有这样的值,即 #VALUE! 并且值少于 5 个要取中位数,那么程序应该只取任何数量的可用值的中位数。 类似地,对于最后一个中值,如果可取中值的值少于 5 个,则应使用可用值的个数计算中值。

可从此处下载包含一列示例数据的 .csv 文件的链接。

LINK to FILE

如果有人可以帮助我,我将非常感激。

谢谢

【问题讨论】:

#VALUE! 错误的存在不是 R 的事情,我相信那只是 Excel 的事情。如果您看到了,您是否使用了一些 R/Excel 集成?无论如何,“一个序列中的五个值”听起来像是一个滚动计算,使用zoo::rollmedian(通常为zoo::rollapply)或slider-package 等效函数之一(我不太熟悉)很容易完成。旁注:许多人不喜欢点击外部链接,链接会过时,在问题本身中有示例数据会很有帮助。见***.com/q/5963269。谢谢! 【参考方案1】:

你可以试试这个:

    按 5 行序列分组 将#VALUE! 替换为NA 转换为数字 用中位数总结
Speed %>% 
  group_by(group5 = rep(row_number(), each=5, length.out = n())) %>% 
  mutate(speed = ifelse(speed== "#VALUE!", NA, speed)) %>% 
  type.convert(as.is = TRUE) %>% 
  summarise(median = median(speed, na.rm = TRUE))
  group5 median
    <int>  <dbl>
 1      1   1.32
 2      2  -4.97
 3      3 -13.1 
 4      4 -14.3 
 5      5   6.89
 6      6  -2.97
 7      7 -11.6 
 8      8 -16.0 
 9      9 -18.6 
10     10 -19.9 
# ... with 72 more rows

【讨论】:

谢谢。输出正是我想要的,但是当我尝试运行代码时,它给了我以下错误: group_by(., group = rep(row_number(), each = 5, length.out = n())) 中的错误:找不到对象“速度”如何纠正? TarJae 在他们的控制台中将变量命名为 Speed。您在控制台中将其命名为什么? Speed 是数据框的名称。 speed 是列名! 啊,明白了。对不起,我犯了一个愚蠢的错误。非常感谢【参考方案2】:

更新“翻滚窗口”(顶部,此处)与“滚动窗口”(下方,留作后代/参考)。仍在使用滚动窗口讨论顶部的dat

关于#VALUE!(远在下面)的讨论可能仍然相关,我将在此处包含代码。

基础 R

dat$speed <- suppressWarnings(as.numeric(dat$speed))
aggregate(dat$speed, list(grp = (seq_len(nrow(dat)) - 1) %/% 5), FUN = median, na.rm = TRUE)
#   grp       x
# 1   0  3.4245
# 2   1 -4.9730

dplyr

library(dplyr)
dat %>%
  mutate(speed = suppressWarnings(as.numeric(speed))) %>%
  group_by(grp = (seq_len(n()) - 1) %/% 5) %>%
  summarize(med5 = median(speed, na.rm = TRUE))
# # A tibble: 2 x 2
#     grp  med5
#   <dbl> <dbl>
# 1     0  3.42
# 2     1 -4.97

数据表

library(data.table)
as.data.table(dat)[, speed := suppressWarnings(as.numeric(speed))
  ][, .(med5 = median(speed, na.rm = TRUE)), by = .(grp = (seq_len(nrow(dat)) - 1) %/% 5)][]
#      grp    med5
#    <num>   <num>
# 1:     0  3.4245
# 2:     1 -4.9730

(下面是滚动窗口,除了dat数据的定义之外不再相关。)


我从该单列框架中复制了前 10 行,然后得到

dat <- structure(list(speed = c(0, 5.534, 1.315, 7.6865, -0.479, -0.4605, -4.311, -4.973, -7.69, -11.669)), row.names = c("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"), class = "data.frame")

有了这个,基本的操作是这样的:

newvec <- zoo::rollmedian(dat$speed, 5)
newvec
# [1]  1.3150  1.3150 -0.4605 -0.4790 -4.3110 -4.9730

请注意,这会创建 nrow(dat) - (k - 1) (20) 值,其中 k=5(您的窗口)。一般来说,滚动操作倾向于减少,但如果需要,我们可以选择改变它。例如,我们可以保持它相同的长度,并用NA 填充两端;为此,我们可以将窗口“对齐”左、中或右:

zoo::rollmedian(dat$speed, 5, fill = NA, align = "left")
#  [1]  1.3150  1.3150 -0.4605 -0.4790 -4.3110 -4.9730      NA      NA      NA      NA
zoo::rollmedian(dat$speed, 5, fill = NA, align = "center")
#  [1]      NA      NA  1.3150  1.3150 -0.4605 -0.4790 -4.3110 -4.9730      NA      NA
zoo::rollmedian(dat$speed, 5, fill = NA, align = "right")
#  [1]      NA      NA      NA      NA  1.3150  1.3150 -0.4605 -0.4790 -4.3110 -4.9730

让我来展示对齐方式如何适合这里。使用align="center"(默认),我们看到:

dat$speed
#  [1]   0.0000   5.5340   1.3150   7.6865  -0.4790  -0.4605  -4.3110  -4.9730  -7.6900 -11.6690
###      `----------------------------------------'
###          take the median of these values,
###          and then assign that single value here:
###                                              /
###                             ,---------------'
###                            / 
#  [1]       NA       NA   1.3150   1.3150  -0.4605  -0.4790  -4.3110  -4.9730       NA       NA

dat$speed[1:5]
# [1]  0.0000  5.5340  1.3150  7.6865 -0.4790
median(dat$speed[1:5])
# [1] 1.315

对于下一个值,

#  [1]   0.0000   5.5340   1.3150   7.6865  -0.4790  -0.4605  -4.3110  -4.9730  -7.6900 -11.6690
###               `----------------------------------------'
###                   take the median of these values,
###                   and then assign that single value here:
###                                                       /
###                                      ,---------------'
###                                     / 
#  [1]       NA       NA   1.3150   1.3150  -0.4605  -0.4790  -4.3110  -4.9730       NA       NA

dat$speed[2:6]
# [1]  5.5340  1.3150  7.6865 -0.4790 -0.4605
median(dat$speed[2:6])
# [1] 1.315

所以我们可以很容易地将它分配给带有data.frame(rollmed = newvec) 的新框架,无论是否有填充。如果您想将其分配回原始框架,可以这样做:

dat$rollmed <- zoo::rollmedian(dat$speed, 5, fill = NA, align = "center")
dat
#       speed rollmed
# 1    0.0000      NA
# 2    5.5340      NA
# 3    1.3150  1.3150
# 4    7.6865  1.3150
# 5   -0.4790 -0.4605
# 6   -0.4605 -0.4790
# 7   -4.3110 -4.3110
# 8   -4.9730 -4.9730
# 9   -7.6900      NA
# 10 -11.6690      NA

至于您的#VALUE!,它可能表现为character 列而不是numeric,在这种情况下,您有一个额外的步骤之前以上所有。

我会将其中一个值更改为该错误:

dat$speed[5] <- "#VALUE!"
dat
#      speed
# 1        0
# 2    5.534
# 3    1.315
# 4   7.6865
# 5  #VALUE!
# 6  -0.4605
# 7   -4.311
# 8   -4.973
# 9    -7.69
# 10 -11.669
str(dat)
# 'data.frame': 10 obs. of  1 variable:
#  $ speed: chr  "0" "5.534" "1.315" "7.6865" ...

(看到了吗?chr。)

从这里,我们可以简单地将所有转换为数字,忽略我们得到的警告:

dat$speed <- suppressWarnings(as.numeric(dat$speed))
dat
#       speed
# 1    0.0000
# 2    5.5340
# 3    1.3150
# 4    7.6865
# 5        NA
# 6   -0.4605
# 7   -4.3110
# 8   -4.9730
# 9   -7.6900
# 10 -11.6690
str(dat)
# 'data.frame': 10 obs. of  1 variable:
#  $ speed: num  0 5.53 1.31 7.69 NA ...

从这里,我们可以再次做滚动中位数。请注意,我们现在的NA 略有变化:

dat$rollmed <- zoo::rollmedian(dat$speed, 5, fill = NA, align = "center")
dat$rollmed2 <- zoo::rollmedian(dat$speed, 5, fill = NA, align = "center", na.rm = TRUE)
dat
#       speed rollmed rollmed2
# 1    0.0000      NA       NA
# 2    5.5340      NA       NA
# 3    1.3150      NA  3.42450
# 4    7.6865      NA  3.42450
# 5        NA      NA  0.42725
# 6   -0.4605      NA -2.38575
# 7   -4.3110      NA -4.64200
# 8   -4.9730  -4.973 -4.97300
# 9   -7.6900      NA       NA
# 10 -11.6690      NA       NA

默认值(我们之前所做的)将为前一个 #VALUE! +/- 4 行 (k-1) 内的每一行返回一个 NA 中值;如果您愿意,我们可以添加na.rm=TRUE;这不是滚动窗口的事情,这是一个一般统计问题,“空值是个问题”。

【讨论】:

谢谢@r2evans 的回答。实际上,我不想像您在基本操作开始时得到的答案那样进行滚动平均。相反,我想做 5 行的平均值,然后是接下来 5 行的平均值。我怎样才能修改代码给我呢? 是的,我误解了这一点。你需要澄清你的问题。顺便说一句,我经常发现翻滚窗口(这就是所谓的)通常与某些东西“对齐”,例如 5Hz 数据上的 1Hz,或工作日数据上的每周等。偶尔在没有“时间”或其他变量的情况下出现翻滚窗口固然有用,但您的数据中是否有任何“时间”成分? 不,我的数据没有“时间”。不过还是谢谢你。在回答主要问题时,您帮助我理解了许多其他事情。非常感谢。

以上是关于在R中顺序计算列的中位数并将值存储在数据框中的主要内容,如果未能解决你的问题,请参考以下文章

R语言vtreat包自动处理dataframe的缺失值使用分组的中位数来标准化数据列中每个数据的值(和中位数表连接并基于中位数进行数据标化)计算数据列的中位数或者均值并进行数据标准化

R:dplyr条件汇总并按列重新编码值

中位数怎么求?

计算表“营销”中“余额”列的中位数

计算数据集列的百分位数

分组数据怎么求中位数