将 dplyr 函数 group_by() 与 cut() 一起使用
Posted
技术标签:
【中文标题】将 dplyr 函数 group_by() 与 cut() 一起使用【英文标题】:Using dplyr function group_by() with cut() 【发布时间】:2021-07-20 01:21:51 【问题描述】:我有一组房地产数据。我正在尝试在市场组(标记为 DOM_Groups)上创建一个新的天数列,并将它们分组为 15 天的间隔(即 0-14、15-29 等)。然后我尝试通过观察次数和每个 15 天组的平均售价来 summarize()
这些分组。
我正在使用cut()
函数试图将我的 DOM_Groups 分成这 15 天的间隔。在我导入的基本电子表格中,包含上市天数的列在每个单元格中都有一个唯一的观察值,并且该列中的数据是数字整数...没有小数,没有负数。
当我运行以下代码时,tibble 输出没有正确分组,它包含一个带小数的负数,这在我的数据集中不存在。我不知道该怎么做才能纠正这个问题。
gibbsMkt %>%
mutate(DOM_Groups = cut(DOM, breaks = 15, dig.lab = 2)) %>%
filter(Status == "SOLD") %>%
group_by(DOM_Groups) %>%
summarize(numDOM = n(),
avgSP = mean(`Sold Price`, na.rm = TRUE))
The tibble output I get is this:
DOM_Groups numDOM avgSP
<fct> <int> <dbl>
1 (-0.23,16] 74 561675.
2 (16,31] 18 632241.
3 (31,47] 11 561727.
4 (47,63] 8 545862.
5 (63,78] 7 729286.
6 (78,94] 6 624167.
7 (1.4e+02,1.6e+02] 2 541000
8 (1.6e+02,1.7e+02] 1 535395
另外,对于 tibble 中的第 7 行和第 8 行,最大的数字是 164,所以我也不明白为什么要将这些行转换为科学计数法。
当我使用 Excel 数据透视表时,我得到了想要在 R 中重现的输出,如下所示:
如何使用正确的代码在 R 中重现这一点?
【问题讨论】:
这些只是标签,并不意味着你有负数:levels(cut(rpois(1000, 1), breaks = 15))
和 breaks = 15
意味着你会得到 15 个区间,而不是将数据切割成 15 个单位的区间
抱歉,这不是粗鲁,但这并不能帮助我理解如何使用正确的代码在 R 中从 Excel 重新创建数据透视表。由于我想要 15 天的小组,我该如何完成?另外,为什么小标题显示的是负数?
【参考方案1】:
cut(x, breaks = 15)
表示x
将被切割成 15 个间隔——它无法猜测您想要从 0 开始到 150 结束的 15 个单位间隔。这是在?cut
的文档中:
breaks
两个或多个唯一切割点的数字向量或单个数字(大于或等于 2)给出 x 将被切割成的区间数。
您需要为每个间隔定义自己的开始和结束,例如:
seq(0, max(x), 15)
# [1] 0 15 30 45 60 75 90 105 120 135 150
cut(x, seq(0, max(x), 15))
但是,如果设置正确,您可以同时定义间隔和制作标签。
set.seed(1)
x <- floor(runif(500, 0, 164))
from <- seq(0, max(x), 15)
to <- from + 15 - 1
labs <- sprintf('%s-%s', from, to)
# [1] "0-14" "15-29" "30-44" "45-59" "60-74" "75-89" "90-104" "105-119" "120-134" "135-149" "150-164"
data.frame(table(cut(x, c(from, Inf), right = FALSE)), labels = labs)
# Var1 Freq labels
# 1 [0,15) 35 0-14
# 2 [15,30) 57 15-29
# 3 [30,45) 45 30-44
# 4 [45,60) 44 45-59
# 5 [60,75) 57 60-74
# 6 [75,90) 55 75-89
# 7 [90,105) 33 90-104
# 8 [105,120) 47 105-119
# 9 [120,135) 40 120-134
# 10 [135,150) 39 135-149
# 11 [150,Inf) 48 150-164
DOM_Groups <- cut(x, c(from, Inf), labs, right = FALSE)
data.frame(table(DOM_Groups))
# DOM_Groups Freq
# 1 0-14 35
# 2 15-29 57
# 3 30-44 45
# 4 45-59 44
# 5 60-74 57
# 6 75-89 55
# 7 90-104 33
# 8 105-119 47
# 9 120-134 40
# 10 135-149 39
# 11 150-164 48
您的另一个问题是“为什么我得到负数”,正如我所提到的,这并不意味着您的数据中有负数 - 这些只是通过使用 breaks = 15
与您的数据生成的标签。
这些是cut.default
中的相关行
if (length(breaks) == 1L)
if (is.na(breaks) || breaks < 2L)
stop("invalid number of intervals")
nb <- as.integer(breaks + 1)
dx <- diff(rx <- range(x, na.rm = TRUE))
if (dx == 0)
dx <- if (rx[1L] != 0)
abs(rx[1L])
else 1
breaks <- seq.int(rx[1L] - dx/1000, rx[2L] + dx/1000,
length.out = nb)
else
breaks <- seq.int(rx[1L], rx[2L], length.out = nb)
breaks[c(1L, nb)] <- c(rx[1L] - dx/1000, rx[2L] +
dx/1000)
使用之前的x
和breaks = 15
,您可以看到如何引入负数:
breaks <- 15
nb <- as.integer(breaks + 1)
dx <- diff(rx <- range(x, na.rm = TRUE))
if (dx == 0)
dx <- if (rx[1L] != 0)
abs(rx[1L])
else 1
breaks <- seq.int(rx[1L] - dx/1000, rx[2L] + dx/1000,
length.out = nb)
else
breaks <- seq.int(rx[1L], rx[2L], length.out = nb)
breaks[c(1L, nb)] <- c(rx[1L] - dx/1000, rx[2L] + dx/1000)
breaks
# [1] -0.16300 10.86667 21.73333 32.60000 43.46667 54.33333 65.20000 76.06667 86.93333 97.80000 108.66667 119.53333 130.40000
# [14] 141.26667 152.13333 163.16300
levels(cut(x, breaks = 15))
# [1] "(-0.163,10.9]" "(10.9,21.7]" "(21.7,32.6]" "(32.6,43.5]" "(43.5,54.3]" "(54.3,65.2]" "(65.2,76.1]" "(76.1,86.9]"
# [9] "(86.9,97.8]" "(97.8,109]" "(109,120]" "(120,130]" "(130,141]" "(141,152]" "(152,163]"
【讨论】:
感谢您的详细回复!目前这有点超出我的编码技能水平,所以我将保存它以供将来参考。您知道是否有一种方法可以像 Excel 数据透视表那样显示每个单独观察的计数在列总数中的百分比? @EastBeast 我不知道 excel 数据透视表的外观,您可以使用paste0(round(proportions(table(DOM_Groups)) * 100), '%')
之类的东西获得百分比【参考方案2】:
这是我的santoku
包的简单解决方案:
library(santoku)
gibbsMkt %>%
mutate(DOM_Groups = chop_width(DOM, 15, labels = lbl_dash("-")))
# then proceed as before
如果您想以特定数字开始间隔,您可以使用 start
参数到 chop_width
。
【讨论】:
嘿,这个包真的很有帮助!谢谢你。有没有办法对组中的最后一个数字设置限制? 如果您想同时控制开始和结束,请执行chop(DOM, seq(start, end, 15), ...)
。但我会考虑功能请求。以上是关于将 dplyr 函数 group_by() 与 cut() 一起使用的主要内容,如果未能解决你的问题,请参考以下文章
R语言dplyr包使用dplyr函数使用group_by函数summarise函数和mutate函数计算分组占比实战
R语言dplyr包使用group_by函数和summarise函数构建频率表实战
在 R 中使用 dplyr 在 group_by 之后应用自定义函数
R语言dplyr包使用group_by函数arrange函数和filter函数获取每个分组的第一个第N个最后一个记录实战