Susun ulang tingkat faktor tanpa mengubah urutan nilai

124

Saya memiliki bingkai data dengan beberapa variabel numerik dan beberapa factorvariabel kategori . Urutan level untuk faktor-faktor tersebut bukanlah seperti yang saya inginkan.

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

Jika saya mengubah urutan level, huruf tidak lagi dengan nomor yang sesuai (data saya benar-benar tidak masuk akal mulai saat ini).

levels(df$letters) <- c("d", "c", "b", "a")
df
#   numbers letters
# 1       1       d
# 2       2       c
# 3       3       b
# 4       4       a

Saya hanya ingin mengubah urutan level , jadi saat memplot, bilah ditampilkan dalam urutan yang diinginkan - yang mungkin berbeda dari urutan alfabet default.

crangos
sumber
1
Bisakah seseorang memberi saya petunjuk mengapa penugasan ke level (...) mengubah urutan entri dalam bingkai data, seperti yang ditunjukkan crangos dalam pertanyaan? Tampaknya sangat tidak intuitif dan tidak diinginkan bagi saya. Saya menghabiskan beberapa waktu untuk men-debug masalah yang disebabkan oleh hari ini sendiri. Saya berpikir mungkin ada alasan untuk perilaku ini yang tidak dapat saya lihat, atau setidaknya penjelasan yang masuk akal mengapa itu terjadi.
Anton

Jawaban:

120

Gunakan levelsargumen factor:

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
Jonathan Chang
sumber
1
Terima kasih, ini berhasil. Untuk beberapa alasan aneh, ggplot sekarang mengubah urutan dalam legenda dengan benar, tetapi tidak dalam plot. Aneh.
crangos
7
ggplot2 mengharuskan saya untuk mengubah keduanya, urutan level (lihat di atas) dan urutan nilai bingkai data. df <- df [nrow (df): 1,] # reverse
crangos
@crangos, saya pikir ggplot menggunakan urutan level menurut abjad, dan terkadang mengabaikan level faktor kustom. Harap konfirmasi, dan sertakan nomor versi.
smci
22

beberapa lagi, hanya sebagai catatan

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

Anda juga dapat menemukan berguna Relevel dan combine_factor .

George Dontas
sumber
2
Jawaban pertamamu tidak berhasil untukku. Tapi ini berhasil:reorder(df$letters, seq(4,1))
Alex Holcombe
1
Saya memiliki situasi yang sangat aneh di mana ´preorder´ bekerja pada satu dataset, bukan pada dataset lainnya. Pada set data lain, ini memunculkan kesalahan "Error in tapply (X = X, INDEX = x, FUN = FUN, ...): argumen" X "tidak ada, tanpa default". Tidak yakin apa solusi untuk masalah ini. Saya tidak dapat menemukan perbedaan yang relevan di antara kumpulan data.
CoderGuy123
10

Sejak pertanyaan ini terakhir aktif, Hadley telah merilis forcatspaket barunya untuk memanipulasi faktor dan saya merasa sangat berguna. Contoh dari kerangka data OP:

levels(df$letters)
# [1] "a" "b" "c" "d"

Untuk membalikkan level:

library(forcats)
fct_rev(df$letters) %>% levels
# [1] "d" "c" "b" "a"

Untuk menambahkan lebih banyak level:

fct_expand(df$letters, "e") %>% levels
# [1] "a" "b" "c" "d" "e"

Dan masih banyak lagi fct_xxx()fungsi yang berguna .

Joe
sumber
Apakah ini masih tersedia?
Joshua Rosenberg
1
Anda ingin menulis kode seperti ini: df %>% mutate(letters = fct_rev(letters)).
jazzurro
9

jadi apa yang Anda inginkan, dalam leksikon R, adalah mengubah hanya label untuk variabel faktor tertentu (yaitu, biarkan data serta level faktor tidak berubah).

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

mengingat bahwa Anda hanya ingin mengubah pemetaan titik-ke-label dan bukan data atau skema faktor (bagaimana titik data digabungkan ke dalam nampan individu atau nilai faktor, mungkin membantu untuk mengetahui bagaimana pemetaan awalnya ditetapkan saat Anda pertama kali membuat faktor.

aturannya sederhana:

  • label dipetakan ke tingkat berdasarkan nilai indeks (yaitu, nilai pada tingkat [2] diberi label, label [2]);
  • level faktor dapat disetel secara eksplisit dengan meneruskannya melalui argumen level ; atau
  • jika tidak ada nilai yang diberikan untuk argumen level, nilai default digunakan yang merupakan hasil panggilan unik pada vektor data yang diteruskan (untuk argumen data );
  • label dapat disetel secara eksplisit melalui argumen label; atau
  • jika tidak ada nilai yang diberikan untuk argumen label, nilai default yang digunakan hanya vektor level
doug
sumber
1
Saya tidak tahu mengapa ini tidak dipilih sebagai jawaban yang diterima. Ini jauh lebih informatif.
Rambatino
12
Jika Anda menggunakan pendekatan ini, data Anda salah diberi label.
Nazer
4
sebenarnya ya saya tidak tahu harus berbuat apa dengan ini, jawabannya sepertinya bermaksud untuk memberi label yang salah pada data demi plot? ugh. digulung kembali ke aslinya. pengguna berhati
rawr
7

Berurusan dengan faktor di R adalah pekerjaan yang cukup aneh, saya harus akui ... Saat menyusun ulang tingkat faktor, Anda tidak menyusun ulang nilai numerik yang mendasarinya. Berikut sedikit demonstrasi:

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

Sekarang, jika Anda mengonversi faktor ini menjadi numerik, Anda akan mendapatkan:

# 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

Seperti yang Anda lihat ... dengan mengubah level, Anda hanya mengubah level (siapa yang tahu, eh?), Bukan nilai numerik! Tapi, ketika Anda menggunakan factorfungsi seperti yang disarankan @Jonathan Chang, sesuatu yang berbeda terjadi: Anda mengubah nilai numerik itu sendiri.

Anda mendapatkan kesalahan sekali lagi karena Anda melakukannya levelsdan kemudian mencoba melepaskannya dengan factor. Jangan lakukan itu !!! Jangan tidak menggunakan levelsatau Anda akan mengacaukan segalanya (kecuali Anda tahu persis apa yang Anda lakukan).

Satu saran kecil: hindari menamai objek Anda dengan nama yang identik dengan objek R ( dfadalah fungsi kepadatan untuk distribusi F, lettersberikan huruf alfabet huruf kecil). Dalam kasus khusus ini, kode Anda tidak akan salah, tetapi terkadang bisa ... tetapi ini dapat menimbulkan kebingungan, dan kami tidak menginginkannya, bukan?!? =)

Sebagai gantinya, gunakan sesuatu seperti ini (saya akan pergi dari awal sekali lagi):

> 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

Perhatikan bahwa Anda juga dapat menamai Anda data.framedengan dfdan letterssebagai pengganti g, dan hasilnya akan baik-baik saja. Sebenarnya kode ini identik dengan yang Anda posting, hanya namanya saja yang diubah. Bagian ini factor(dtf$letter, levels = letters[4:1])tidak akan menimbulkan kesalahan, tetapi bisa membingungkan!

Baca ?factormanualnya dengan seksama! Apa perbedaan antara factor(g, levels = letters[4:1])dan factor(g, labels = letters[4:1])? Apa yang mirip dalam levels(g) <- letters[4:1]dan g <- factor(g, labels = letters[4:1])?

Anda dapat menempatkan sintaks ggplot, jadi kami dapat membantu Anda lebih banyak tentang yang satu ini!

Bersulang!!!

Edit:

ggplot2sebenarnya perlu mengubah level dan nilai? Hm ... Aku akan menggali yang ini ...

aL3xa
sumber
3

Saya ingin menambahkan kasus lain di mana level dapat berupa string yang membawa angka bersama dengan beberapa karakter khusus: seperti contoh di bawah ini

df <- data.frame(x = c("15-25", "0-4", "5-10", "11-14", "100+"))

Tingkat default xadalah:

df$x
# [1] 15-25 0-4   5-10  11-14 100+ 
# Levels: 0-4 100+ 11-14 15-25 5-10

Di sini jika kita ingin menyusun ulang level faktor sesuai dengan nilai numerik, tanpa secara eksplisit menuliskan levelnya, yang bisa kita lakukan adalah

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

Semoga ini dapat menjadi informasi yang bermanfaat bagi pembaca selanjutnya.

joel.wilson
sumber
0

Inilah fungsi saya untuk menyusun ulang faktor-faktor dari kerangka data yang diberikan:

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

Pemakaian: reorderFactors(df, "my_col", desired_level_order = c("how","I","want"))

Boern
sumber