在不更改值顺序的情况下对因子的水平进行重新排序

Reorder levels of a factor without changing order of values

提问人:crangos 提问时间:3/4/2010 最后编辑:Waelcrangos 更新时间:8/28/2021 访问量:106347

问:

我有包含一些数值变量和一些分类变量的数据框。这些因素的水平顺序不是我想要的方式。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

我只想更改级别顺序,因此在绘图时,条形图按所需顺序显示 - 这可能与默认字母顺序不同。

排序 级别 R-FAQ

评论

3赞 Anton 2/18/2019
有人可以给我一个提示,为什么分配给级别(...)会改变数据框中条目的顺序,正如问题中所示的crangos?这对我来说似乎非常不直观和不受欢迎。我今天花了一些时间调试由此引起的问题。我认为这种行为可能有一个我看不出来的原因,或者至少对它发生的原因有一个合理的解释。

答:

12赞 doug 3/4/2010 #1

因此,在 R 词典中,您想要的是仅更改给定因子变量的标签(即,保持数据和因子水平不变)。

df$letters = factor(df$letters, labels=c("d", "c", "b", "a"))

假设您只想更改数据点到标签的映射,而不想更改数据或因子架构(如何将数据点装箱到单个图格或因子值中),那么了解最初创建因子时映射的初始设置方式可能会有所帮助。

规则很简单:

  • 标签按索引值(即 在 levels[2] 被赋予标签 label[2]);
  • 可以通过 Levels 参数传入因子水平来显式设置因子水平;或
  • 如果未为 levels 参数提供任何值,则默认 value 是调用数据向量的唯一结果 传入(用于数据参数);
  • 可以通过 labels 参数显式设置标签;或
  • 如果未为 labels 参数提供任何值,则默认值为 使用,这只是水平向量

评论

1赞 Rambatino 4/12/2014
我不知道为什么这不像公认的答案那样被投票赞成。这信息量要大得多。
16赞 Nazer 5/13/2014
如果使用此方法,则数据会被错误标记。
4赞 rawr 11/19/2014
其实是的,我不知道该怎么办,答案似乎是为了绘图而故意错误地标记数据?呸。回滚到原始状态。用户要当心
146赞 Jonathan Chang 3/4/2010 #2

使用以下参数:levelsfactor

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

评论

1赞 crangos 3/4/2010
谢谢,这奏效了。出于某种奇怪的原因,ggplot 现在正确地更改了图例中的顺序,但没有更改图中的顺序。奇怪。
7赞 crangos 3/4/2010
ggplot2 要求我更改级别顺序(见上文)和数据框值的顺序。df <- df[nrow(df):1, ] # 反转
0赞 smci 6/27/2018
@crangos,我认为 ggplot 使用按字母顺序排列的水平,有时会忽略自定义因子水平。请确认,并附上版本号。
25赞 Γιώργος 3/4/2010 #3

还有一些,只是为了记录

## 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])

您还可以找到有用的 Relevelcombine_factor

评论

2赞 Alex Holcombe 8/30/2015
你的第一个答案对我不起作用。但这有效:reorder(df$letters, seq(4,1))
2赞 CoderGuy123 12/28/2015
我有一个非常奇怪的情况,即“重新排序”适用于一个数据集,而不是另一个数据集。在另一个数据集上,它会抛出错误“Error in tapply(X = X, INDEX = x, FUN = FUN, ...) : argument ”X“ is missing, with no default”。不确定此问题的解决方案是什么。我找不到数据集之间的任何相关差异。
8赞 aL3xa 3/4/2010 #4

我必须承认,处理 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

您再次收到错误,因为您这样做了,然后尝试用 重新调平它。别这样!!!不要使用,否则你会把事情搞砸(除非你确切地知道你在做什么)。levelsfactorlevels

一个小小的建议:避免使用与 R 的对象相同的名称来命名对象(df 是 F 分布的密度函数,字母给出小写的字母)。在这种特殊情况下,您的代码不会有错误,但有时它可能会......但这会造成混乱,我们不希望这样,不是吗?!?=)

相反,使用这样的东西(我将再次从头开始):

> 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

请注意,您也可以用 和 代替 来命名您,结果就可以了。 实际上,此代码与您发布的代码相同,只是名称已更改。这部分不会抛出错误,但可能会令人困惑!data.framedflettersgfactor(dtf$letter, levels = letters[4:1])

仔细阅读手册!和 和有什么不一样?和 有什么相似之处??factorfactor(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实际上需要同时更改级别和值?嗯......我会把这个挖出来......

14赞 Joe 10/13/2016 #5

自从这个问题上次出现以来,哈德利已经发布了他用于操纵因子的新软件包,我发现它非常有用。OP 数据帧中的示例:forcats

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()

评论

0赞 Joshua Rosenberg 11/14/2016
这仍然可用吗?
1赞 jazzurro 12/22/2016
您想编写如下代码:.df %>% mutate(letters = fct_rev(letters))
3赞 joel.wilson 1/24/2017 #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

我希望这可以被视为对未来读者有用的信息。

1赞 Boern 4/7/2018 #7

这是我对给定数据帧的因子进行重新排序的函数:

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"))

1赞 Maria 1/29/2021 #8

我只会使用 levels 参数:

levels(df$letters) <- levels(df$letters)[c(4:1)]
0赞 xaviescacs 5/30/2021 #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"