我在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
感谢您的帮助!
答案 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]