R中函数cut和ifelse之间的奇怪行为

时间:2016-01-23 15:42:32

标签: r r-factor

我在R中使用由数字变量和字符变量组成的数据框。我的数据框DF看起来像这样(我在最后部分添加了dput版本):

   a1    b1
1   a 10.15
2   a 25.10
3   a 32.40
4   a 56.70
5   a 89.02
6   b 90.50
7   b 78.53
8   b 98.12
9   b 34.30
10  b 99.75 

DF中,变量a1是一个组变量,b1是一个数字变量。然后出现了困境。我想使用c1函数创建一个名为cut的新变量,并考虑保存在a1中的组。出于这个原因,我在下一行代码中结合了ifelse()cut()两个函数:

DF$c1=ifelse(DF$a1=="a",
                cut(DF$b1,breaks = c(0,25,50,70,max(DF$b1)),right = TRUE,include.lowest = TRUE),
                ifelse(DF$a1=="b",
                       cut(DF$b1,breaks = c(0,50,max(DF$b1)),right = TRUE,include.lowest = TRUE),NA))

代码行工作正常,但是在c1中创建的新值存在令人困惑的结果。 cut()不是显示因子,而是返回整数。然后,我得到了这个结果:

table(DF$c1,exclude=NULL)

   1    2    3    4 <NA> 
   2    6    1    1    0

尽管创建了中断,但c1中分配的整数会改变结果。当我在没有ifelse的情况下工作时,这种情况不会发生,但在这种情况下,我不同意该组的条件。例如,下一行代码返回此结果:

DF$c1=cut(DF$b1,breaks = c(0,25,50,70,max(DF$b1)),right = TRUE,include.lowest = TRUE)

table(DF$c1,exclude=NULL)

   [0,25]   (25,50]   (50,70] (70,99.8]      <NA> 
        1         3         1         5         0 

我想知道如何解决ifelse()cut()函数之间的这种行为,因为返回的整数会在最终结果中产生差异。在这个例子中,我只为a1变量的两个组工作,但我有一个包含许多组的大型数据库。这就是我将这些功能结合起来为每个组获得不同削减的原因。此外,中断的值可能会发生变化,因此以手动方式包含标签可能会很长。这两个函数的组合是否可能为每个组(因子)而不是整数返回正确的标签。我的数据框dput()的{​​{1}}版本是下一个:

DF

感谢您的帮助!

3 个答案:

答案 0 :(得分:3)

问题在于cut()都输出一个因子,但由于它们有不同的级别,因此它们被强制转换为整数。一个解决方案可能是将cut()as.character()包围,从而保留强制级别,然后factor()整个输出:

DF$c1=factor(ifelse(DF$a1=="a",
             as.character(cut(DF$b1,breaks = c(0,25,50,70,max(DF$b1)),right = TRUE,include.lowest = TRUE)),
             ifelse(DF$a1=="b",
                    as.character(cut(DF$b1,breaks = c(0,50,max(DF$b1)),right = TRUE,include.lowest = TRUE)),NA)))

DF

   a1    b1        c1
1   a 10.15    [0,25]
2   a 25.10   (25,50]
3   a 32.40   (25,50]
4   a 56.70   (50,70]
5   a 89.02 (70,99.8]
6   b 90.50 (50,99.8]
7   b 78.53 (50,99.8]
8   b 98.12 (50,99.8]
9   b 34.30    [0,50]
10  b 99.75 (50,99.8]

答案 1 :(得分:2)

@scoa是对的;你试图将两个不同级别的因素结合起来,这样你的结果就会被强制转换为整数,而你却失去了这个级别。这是另一种具有更小外形尺寸的方法,它将更具可扩展性。

首先,列出所有休息时间的命名列表:

breaks <- list('a' = c(0, 25, 50, 70, max(DF$b1)), 'b' = c(0, 50, max(DF$b1)))
breaks

> $a
>     0 25 50 70 99.75 
> $b
>     0 50 99.75 

然后使用unlist(list(some, factors))(或者在这种情况下,lapply),它整齐地合并因子,保持所有级别。 (这有点神奇;它是那些内置功能之一,并不是很明显。)

DF$c1 <- unlist(lapply(1:length(breaks), 
                   function(x){cut(DF[DF$a1 == names(breaks[x]), 'b1'], 
                                   breaks = breaks[[x]], 
                                   right = TRUE, 
                                   include.lowest = TRUE)}
                   ))
DF

>    a1    b1        c1
> 1   a 10.15    [0,25]
> 2   a 25.10   (25,50]
> 3   a 32.40   (25,50]
> 4   a 56.70   (50,70]
> 5   a 89.02 (70,99.8]
> 6   b 90.50 (50,99.8]
> 7   b 78.53 (50,99.8]
> 8   b 98.12 (50,99.8]
> 9   b 34.30    [0,50]
> 10  b 99.75 (50,99.8]

它最终有两行代码,并且应该在更大,更复杂的数据集上运行。

答案 2 :(得分:2)

这不是您问题的直接答案,而是整体任务的替代方法。

因为你有“一个包含许多组的大型数据库[对每个组有不同的剪切”,所以在我看来,很多嵌套ifelse的代码很快就会变得非常混乱。也许是一种品味问题,但我认为如果将每个组的breaks存储在一个单独的表中,代码将更容易阅读和维护。

以下是使用data.table

执行此操作的方法
library(data.table)
dt_brk <- data.table(grp = c("a", "a", "a", "a", "a", "b", "b", "b"),
                     brk = c(0, 25, 50, 70, Inf, 0, 50, Inf))

请注意,我使用Inf作为休息的上限,而不是max(your-values)

我们使用data.table将您的数据框“DF”转换为setDT。然后,对于每个级别的“a1”(by = a1),我们cut“b1”,使用“dt_brk”中的breaks,其中“grp”等于“a1”({{1 }})。

dt_brk[grp == a1, brk]