从 `cut` 函数中获取下限和上限作为数值。

12

我在这里阅读了这个问题:将连续的数值转换为由间隔定义的离散类别

然而,我想输出一个数字(而不是因子),具体来说是下限和/或上限的数字值(分别在不同的列中)

本质上,这是正确的,只是'df$start'和'df$end'被给定为因子:

df$start <- cut(df$x, 
                breaks = c(0,25,75,125,175,225,299),
                labels = c(0,25,75,125,175,225),
                right = TRUE)

df$end <- cut(df$x, 
              breaks = c(0,25,75,125,175,225,299),
              labels = c(25,75,125,175,225,299),
              right = TRUE)

使用as.numeric()将返回因子(即值1-6)的级别,而不是原始数字。


4
您可以先使用as.character转换为字符型,然后再使用as.numeric转换为数值型。虽然我感觉应该有更好的解决方法来解决这个问题。 - user295691
4个回答

14

cut命令的大部分行为与创建您不感兴趣的标签相关。使用findInterval或者.bincode可能更好。

您可以从数据开始。

set.seed(17)
df <- data.frame(x=300 * runif(100))

然后设置断点并找出间隔:

breaks <- c(0,25,75,125,175,225,299)
df$interval <- findInterval(df$x, breaks)
df$start <- breaks[df$interval]
df$end <- breaks[df$interval + 1]

1
+1 对于 .bincode。我之前只见过 findInterval。现在正在尝试弄清它们之间的主要区别。看起来在处理断点处的值时有所不同,它们在 findInterval 中被移动到下一个级别,但在 .bincode 中则没有。而且,在 .bincode 中,超出断点的点映射为 NA,在默认参数下,findInterval 中映射为 0 或 N。 - C8H10N4O2
@C8H10N4O2 这个问题让我很好奇,足以让我深入到源代码层面去探究;实际上,这些函数在算法上几乎是相同的,一个有趣的项目就是将它们的实现合并成一个单一的函数,并提供选项来支持两者所需的行为,可能通过转换单个函数的输出来实现。 - user295691

8
我猜测您需要什么,因为如果您想要“原始数字”,那么您可以使用df$x。我猜您想要一个数字来反映组别?根据我的猜测,以下是一些可能的内容。
## Generate some example data
x = runif(5, 0, 300)
## Specify the labels
labels = c(0,25,75,125,175,225)
## Use cut as before
y = cut(x, 
    breaks = c(0,25,75,125,175,225,300),
    labels = labels,
    right = TRUE)

当我们将y转换为数字时,这会给出标签的索引。因此,
labels[as.numeric(y)]

或者更简单
labels[y]

3
实际上,最好是不使用标签来保存间断点,如果我们只需要因子水平,是否使用自动生成的标签并不重要。所以,只需将 df$start <- breaks[cut(df$x, breaks=breaks, right=TRUE)] - user295691
谢谢你们两位。你的回答和评论都解决了问题 @user295691 - Andrew

5
我会选择使用正则表达式,因为所有信息都在 cut 命令的输出中。
cut_borders <- function(x){
pattern <- "(\\(|\\[)(-*[0-9]+\\.*[0-9]*),(-*[0-9]+\\.*[0-9]*)(\\)|\\])"

start <- as.numeric(gsub(pattern,"\\2", x))
end <- as.numeric(gsub(pattern,"\\3", x))

data.frame(start, end)
}

单词模式:

  • 第一组: 要么是一个 ( 要么是一个 [,所以我们使用 (\\(|\\[)

  • 第二组: 数字可能是负数,所以我们使用 (-*),我们寻找至少一个数字 ([0-9]+),它可以有小数点,即点号 (\\.*) 和小数点后面的数字 ([0-9]*)。

  • 接下来是一个逗号 (,)

  • 第三组:与第二组相同。

  • 第四组:类似于第一组,我们期望的是要么是一个 ),要么是一个 ]

这里有一些用分位数划分的随机变量。函数 cut_borders 返回我们要查找的内容:

x <- rnorm(10)

x_groups <- cut(x, quantile(x, 0:4/4), include.lowest= TRUE)

cut_borders(x_groups)

很好,但是正则表达式可以大幅缩短。我们对截取模式非常了解。因此,我们可以使用 ".{1}(-?\\d+),(-?\\d+).{1}" - 无需寻找 [ 或 (,并且 "-" 可以用 ? 使其变为可选项。使用 \d 比 [0-9] 稍微更短一些。 - tjebo
这个正则表达式无法捕获科学计数法表示的区间,例如(1.26e+03,1.55e+03] - postylem

2
我们可以使用tidyr::extract
library(tidyverse)
set.seed(17)
df <- data.frame(x = cut(300 * runif(100), c(0,25,75,125,175,225,299)))

df %>%
  extract(x, c("start", "end"), "(-?\\d+),(-?\\d+)")
#>     start end
#> 1      25  75
#> 2     225 299
#> 3     125 175
#> 4     225 299
#> 5      75 125
#> 6     125 175
#> ...

此文本由reprex软件包(v2.0.0)于2021年5月11日创建

P.S. 感谢用户295691提供的数据用户machine提供的正则表达式初稿,这里进行了修改。两位用户+1 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接