在不更改值顺序的情况下重新排序因子的级别
Posted
技术标签:
【中文标题】在不更改值顺序的情况下重新排序因子的级别【英文标题】:Reorder levels of a factor without changing order of values 【发布时间】:2011-01-23 10:51:42 【问题描述】:我有一些数值变量和一些分类factor
变量的数据框。这些因素的水平顺序不是我想要的。
numbers <- 1:4
letters <- factor(c("a", "b", "c", "d"))
df <- data.frame(numbers, letters)
df
# numbers letters
# 1 1 a
# 2 2 b
# 3 3 c
# 4 4 d
如果我改变关卡的顺序,字母不再是对应的数字(从现在开始,我的数据完全是一派胡言)。
levels(df$letters) <- c("d", "c", "b", "a")
df
# numbers letters
# 1 1 d
# 2 2 c
# 3 3 b
# 4 4 a
我只是想更改 级别 顺序,因此在绘图时,条形图按所需顺序显示 - 这可能与默认字母顺序不同。
【问题讨论】:
有人可以给我一个提示,为什么分配给级别(...)会改变数据框中条目的顺序,正如 crangos 在问题中显示的那样?这对我来说似乎非常不直观和不受欢迎。我自己今天花了一些时间调试由此引起的问题。我在想这种行为可能有一个我看不到的原因,或者至少对它发生的原因有一个合理的解释。 【参考方案1】:使用factor
的levels
参数:
df <- data.frame(f = 1:4, g = letters[1:4])
df
# f g
# 1 1 a
# 2 2 b
# 3 3 c
# 4 4 d
levels(df$g)
# [1] "a" "b" "c" "d"
df$g <- factor(df$g, levels = letters[4:1])
# levels(df$g)
# [1] "d" "c" "b" "a"
df
# f g
# 1 1 a
# 2 2 b
# 3 3 c
# 4 4 d
【讨论】:
谢谢,这成功了。出于某种奇怪的原因,ggplot 现在正确地更改了图例中的顺序,但不是在情节中。很奇怪。 ggplot2 要求我更改级别的顺序(见上文)和数据框值的顺序。 df @craangos,我认为 ggplot 使用字母顺序的级别,有时会忽略自定义因子级别。请确认,并附上版本号。【参考方案2】:还有一些,只是为了记录
## reorder is a base function
df$letters <- reorder(df$letters, new.order=letters[4:1])
library(gdata)
df$letters <- reorder.factor(df$letters, letters[4:1])
您可能还会发现有用的Relevel 和combine_factor。
【讨论】:
您的第一个答案对我不起作用。但这有效:reorder(df$letters, seq(4,1))
我有一个非常奇怪的情况,即“重新排序”在一个数据集上工作,而不是在另一个数据集上工作。在另一个数据集上,它会引发错误“tapply 中的错误(X = X,INDEX = x,FUN = FUN,...):缺少参数“X”,没有默认值”。不知道这个问题的解决方案是什么。我找不到数据集之间的任何相关差异。【参考方案3】:
自从上次提出这个问题以来,Hadley 发布了他的新 forcats
软件包,用于操纵因素,我发现它非常有用。来自 OP 数据框的示例:
levels(df$letters)
# [1] "a" "b" "c" "d"
反转水平:
library(forcats)
fct_rev(df$letters) %>% levels
# [1] "d" "c" "b" "a"
添加更多关卡:
fct_expand(df$letters, "e") %>% levels
# [1] "a" "b" "c" "d" "e"
还有更多有用的fct_xxx()
函数。
【讨论】:
这个还能用吗? 你想写这样的代码:df %>% mutate(letters = fct_rev(letters))
.【参考方案4】:
因此,在 R 词典中,您想要的只是更改给定因子变量的 标签(即,保留数据以及因子 级别,不变)。
df$letters = factor(df$letters, labels=c("d", "c", "b", "a"))
假设您只想更改数据点到标签的映射,而不是数据或因子架构(数据点如何被分箱到单独的箱或因子值中,了解这可能会有所帮助最初创建因子时如何设置映射。
规则很简单:
标签通过索引值映射到级别(即,值 在 level[2] 处被赋予标签 label[2]); 可以通过通过 levels 参数;或 如果没有为levels参数提供值,默认 使用值,它是在数据向量上调用 unique 的结果 传入(用于 data 参数); 标签可以通过标签参数显式设置;或 如果没有为标签参数提供值,则默认值为 used 这只是 levels 向量【讨论】:
我不知道为什么这没有像接受的答案那样被投票。这提供了更多信息。 如果您使用这种方法,您的数据会被错误标记。 实际上是的,我不知道该怎么办,答案似乎是为了绘图而错误地标记数据?啊。回滚到原来的。用户小心【参考方案5】:处理 R 中的因子是一项非常特殊的工作,我必须承认...在重新排序因子水平时,您并没有重新排序基础数值。这是一个小演示:
> numbers = 1:4
> letters = factor(letters[1:4])
> dtf <- data.frame(numbers, letters)
> dtf
numbers letters
1 1 a
2 2 b
3 3 c
4 4 d
> sapply(dtf, class)
numbers letters
"integer" "factor"
现在,如果您将此因子转换为数字,您将得到:
# return underlying numerical values
1> with(dtf, as.numeric(letters))
[1] 1 2 3 4
# change levels
1> levels(dtf$letters) <- letters[4:1]
1> dtf
numbers letters
1 1 d
2 2 c
3 3 b
4 4 a
# return numerical values once again
1> with(dtf, as.numeric(letters))
[1] 1 2 3 4
如您所见...通过更改级别,您仅更改级别(谁会告诉,嗯?),而不是数值!但是,当您按照 @Jonathan Chang 的建议使用 factor
函数时,会发生不同的事情:您自己更改数值。
您再次遇到错误,因为您执行了levels
,然后尝试使用factor
重新调整它。不要这样做!不要不要使用levels
,否则你会搞砸(除非你确切地知道你在做什么)。
一个小建议:避免使用与 R 对象相同的名称来命名对象(df
是 F 分布的密度函数,letters
给出小写字母)。在这种特殊情况下,您的代码不会有错误,但有时可能会……但这会造成混乱,我们不希望这样,不是吗?!? =)
相反,使用这样的东西(我将再次从头开始):
> dtf <- data.frame(f = 1:4, g = factor(letters[1:4]))
> dtf
f g
1 1 a
2 2 b
3 3 c
4 4 d
> with(dtf, as.numeric(g))
[1] 1 2 3 4
> dtf$g <- factor(dtf$g, levels = letters[4:1])
> dtf
f g
1 1 a
2 2 b
3 3 c
4 4 d
> with(dtf, as.numeric(g))
[1] 4 3 2 1
请注意,您也可以使用df
和letters
来命名data.frame
,而不是g
,结果就可以了。实际上,此代码与您发布的代码相同,只是名称有所不同。这部分factor(dtf$letter, levels = letters[4:1])
不会抛出错误,但它可能会令人困惑!
彻底阅读?factor
手册! factor(g, levels = letters[4:1])
和 factor(g, labels = letters[4:1])
有什么区别? levels(g) <- letters[4:1]
和 g <- factor(g, labels = letters[4:1])
有什么相似之处?
您可以输入 ggplot 语法,这样我们可以在这方面为您提供更多帮助!
干杯!!!
编辑:
ggplot2
实际上需要同时更改级别和值?嗯……我把这个挖出来……
【讨论】:
【参考方案6】:我希望添加另一种情况,其中级别可以是带有数字和一些特殊字符的字符串:如下例所示
df <- data.frame(x = c("15-25", "0-4", "5-10", "11-14", "100+"))
x
的默认级别是:
df$x
# [1] 15-25 0-4 5-10 11-14 100+
# Levels: 0-4 100+ 11-14 15-25 5-10
这里如果我们想根据数值对因子水平重新排序,而不明确写出水平,我们可以做的是
library(gtools)
df$x <- factor(df$x, levels = mixedsort(df$x))
df$x
# [1] 15-25 0-4 5-10 11-14 100+
# Levels: 0-4 5-10 11-14 15-25 100+
as.numeric(df$x)
# [1] 4 1 2 3 5
我希望这可以被视为对未来读者有用的信息。
【讨论】:
【参考方案7】:我会简单地使用级别参数:
levels(df$letters) <- levels(df$letters)[c(4:1)]
【讨论】:
【参考方案8】:这是我对给定数据框的因子重新排序的函数:
reorderFactors <- function(df, column = "my_column_name",
desired_level_order = c("fac1", "fac2", "fac3"))
x = df[[column]]
lvls_src = levels(x)
idxs_target <- vector(mode="numeric", length=0)
for (target in desired_level_order)
idxs_target <- c(idxs_target, which(lvls_src == target))
x_new <- factor(x,levels(x)[idxs_target])
df[[column]] <- x_new
return (df)
用法:reorderFactors(df, "my_col", desired_level_order = c("how","I","want"))
【讨论】:
【参考方案9】:添加另一种非常有用的方法,因为它使我们不必记住来自不同包的函数。因子的水平只是属性,因此可以执行以下操作:
numbers <- 1:4
letters <- factor(c("a", "b", "c", "d"))
df <- data.frame(numbers, letters)
# Original attributes
> attributes(df$letters)
$levels
[1] "a" "b" "c" "d"
$class
[1] "factor"
# Modify attributes
attr(df$letters,"levels") <- c("d", "c", "b", "a")
> df$letters
[1] d c b a
Levels: d c b a
# New attributes
> attributes(df$letters)
$levels
[1] "d" "c" "b" "a"
$class
[1] "factor"
【讨论】:
以上是关于在不更改值顺序的情况下重新排序因子的级别的主要内容,如果未能解决你的问题,请参考以下文章
带有geom_tile的热图如何在不使用构面的情况下根据另一个因素进行划分和重新排序
Movilizer:在不更改数组键的情况下对数据容器进行排序
我是否可以在不立即呈现更改的情况下更新Firestore文档?