Bagaimana cara menetapkan warna ke variabel kategori di ggplot2 yang memiliki pemetaan stabil?

178

Saya sudah bangun dengan kecepatan R di bulan lalu.

Ini pertanyaan saya:

Apa cara yang baik untuk menetapkan warna ke variabel kategori di ggplot2 yang memiliki pemetaan stabil? Saya membutuhkan warna yang konsisten di seluruh set grafik yang memiliki himpunan bagian yang berbeda dan jumlah variabel kategori yang berbeda.

Sebagai contoh,

plot1 <- ggplot(data, aes(xData, yData,color=categoricaldData)) + geom_line()

dimana categoricalDatamemiliki 5 level.

Lalu

plot2 <- ggplot(data.subset, aes(xData.subset, yData.subset, 
                                 color=categoricaldData.subset)) + geom_line()

dimana categoricalData.subsetmemiliki 3 level.

Namun, level tertentu yang ada di kedua set akan berakhir dengan warna yang berbeda, yang membuatnya lebih sulit untuk membaca grafik bersama.

Apakah saya perlu membuat vektor warna dalam bingkai data? Atau adakah cara lain untuk menetapkan warna tertentu ke dalam kategori?

musim dingin
sumber

Jawaban:

187

Untuk situasi sederhana seperti contoh persis dalam OP, saya setuju bahwa jawaban Thierry adalah yang terbaik. Namun, saya pikir ini berguna untuk menunjukkan pendekatan lain yang menjadi lebih mudah ketika Anda mencoba mempertahankan skema warna yang konsisten di beberapa frame data yang tidak semuanya diperoleh dengan berlangganan satu frame data besar. Mengelola level faktor dalam beberapa frame data dapat menjadi membosankan jika mereka ditarik dari file terpisah dan tidak semua level faktor muncul di setiap file.

Salah satu cara untuk mengatasinya adalah dengan membuat skala warna manual kustom sebagai berikut:

#Some test data
dat <- data.frame(x=runif(10),y=runif(10),
        grp = rep(LETTERS[1:5],each = 2),stringsAsFactors = TRUE)

#Create a custom color scale
library(RColorBrewer)
myColors <- brewer.pal(5,"Set1")
names(myColors) <- levels(dat$grp)
colScale <- scale_colour_manual(name = "grp",values = myColors)

dan kemudian tambahkan skala warna ke plot sesuai kebutuhan:

#One plot with all the data
p <- ggplot(dat,aes(x,y,colour = grp)) + geom_point()
p1 <- p + colScale

#A second plot with only four of the levels
p2 <- p %+% droplevels(subset(dat[4:10,])) + colScale

Plot pertama terlihat seperti ini:

masukkan deskripsi gambar di sini

dan plot kedua terlihat seperti ini:

masukkan deskripsi gambar di sini

Dengan cara ini Anda tidak perlu mengingat atau memeriksa setiap frame data untuk melihat bahwa mereka memiliki level yang sesuai.

joran
sumber
1
Ini akan berhasil, tetapi mungkin terlalu rumit. Saya tidak berpikir Anda perlu membuat skala manual untuk ini. Yang Anda butuhkan adalah factoryang umum di antara semua plot.
Andrie
14
@ Andrie - Untuk satu bagian, ya. Tetapi jika Anda menyulap banyak set data yang tidak semuanya dibuat dengan berlangganan satu frame data asli, saya menemukan strategi ini jauh lebih sederhana.
joran
2
@ joran Terima kasih Joran. Ini berhasil untuk saya! Ini menciptakan legenda dengan jumlah faktor yang tepat. Saya suka pendekatan dan untuk mendapatkan pemetaan warna di set data yang berbeda adalah layak tiga baris.
musim dingin
3
Saya membutuhkan: library ("RColorBrewer")
PatrickT
4
bekerja dengan sempurna! Saya menambahkan fillScale <- scale_fill_manual(name = "grp",values = myColors)untuk menggunakan ini dengan plot bar.
pentandrous
42

Saya dalam situasi yang sama ditunjukkan oleh malcook dalam komentarnya : sayangnya jawaban oleh Thierry tidak bekerja dengan ggplot2 versi 0.9.3.1.

png("figure_%d.png")
set.seed(2014)
library(ggplot2)
dataset <- data.frame(category = rep(LETTERS[1:5], 100),
    x = rnorm(500, mean = rep(1:5, 100)),
    y = rnorm(500, mean = rep(1:5, 100)))
dataset$fCategory <- factor(dataset$category)
subdata <- subset(dataset, category %in% c("A", "D", "E"))

ggplot(dataset, aes(x = x, y = y, colour = fCategory)) + geom_point()
ggplot(subdata, aes(x = x, y = y, colour = fCategory)) + geom_point()

Ini dia gambar pertama:

ggplot AE, warna campuran

dan gambar kedua:

ggplot ADE, warna campuran

Seperti yang bisa kita lihat, warnanya tidak tetap, misalnya E beralih dari magenta ke blu.

Seperti yang disarankan oleh malcook dalam komentarnya dan oleh hadley dalam komentarnya kode yang menggunakan limitsberfungsi dengan benar:

ggplot(subdata, aes(x = x, y = y, colour = fCategory)) +       
    geom_point() + 
    scale_colour_discrete(drop=TRUE,
        limits = levels(dataset$fCategory))

memberikan gambar berikut, yang benar:

ggplot yang benar

Ini adalah output dari sessionInfo():

R version 3.0.2 (2013-09-25)
Platform: x86_64-pc-linux-gnu (64-bit)

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] methods   stats     graphics  grDevices utils     datasets  base     

other attached packages:
[1] ggplot2_0.9.3.1

loaded via a namespace (and not attached):
 [1] colorspace_1.2-4   dichromat_2.0-0    digest_0.6.4       grid_3.0.2        
 [5] gtable_0.1.2       labeling_0.2       MASS_7.3-29        munsell_0.4.2     
 [9] plyr_1.8           proto_0.3-10       RColorBrewer_1.0-5 reshape2_1.2.2    
[13] scales_0.2.3       stringr_0.6.2 
Alessandro Jacopson
sumber
3
Anda harus memposting ini sebagai pertanyaan baru, merujuk pertanyaan ini dan menunjukkan mengapa solusi di sini tidak berhasil.
Brian Diggs
Pertanyaan serupa diajukan di sini , tetapi saya ingin menunjukkan bahwa jawaban yang diterima berfungsi dengan baik.
tonytonov
1
Jadi saya tahu ini sudah tua tapi saya ingin tahu apakah ada cara untuk melakukan ini tanpa memiliki warna tambahan dalam legenda.
goryh
20

Solusi termudah adalah mengonversi variabel kategori Anda menjadi faktor sebelum berlangganan. Intinya adalah bahwa Anda memerlukan variabel faktor dengan tingkat yang sama persis di semua himpunan bagian Anda.

library(ggplot2)
dataset <- data.frame(category = rep(LETTERS[1:5], 100), 
    x = rnorm(500, mean = rep(1:5, 100)), y = rnorm(500, mean = rep(1:5, 100)))
dataset$fCategory <- factor(dataset$category)
subdata <- subset(dataset, category %in% c("A", "D", "E"))

Dengan variabel karakter

ggplot(dataset, aes(x = x, y = y, colour = category)) + geom_point()
ggplot(subdata, aes(x = x, y = y, colour = category)) + geom_point()

Dengan variabel faktor

ggplot(dataset, aes(x = x, y = y, colour = fCategory)) + geom_point()
ggplot(subdata, aes(x = x, y = y, colour = fCategory)) + geom_point()
Thierry
sumber
11
Cara termudah adalah dengan menggunakan batas
Hadley
1
Bisakah memberikan contoh dalam konteks ini Hadley? Saya tidak yakin bagaimana menggunakan batasan dengan sebuah faktor.
Thierry
@Terima kasih. Saya senang mendapat tanggapan pada posting pertama saya. Dan terima kasih Thierry atau menambahkan dalam kode yang dapat direproduksi seperti seharusnya di pos saya ... Variabel kategori saya adalah tipe - faktor yang tepat. Masalah lainnya adalah saya ingin legenda tidak menunjukkan faktor yang tidak digunakan. R mengabaikan variabel karakter yang tidak digunakan saat membangun legenda. Namun, faktor yang tidak digunakan tetap ada. Jika saya menjatuhkan mereka menggunakan: subdata $ kategori <- faktor (subdata $ kategori) [drop = BENAR] maka legenda memiliki jumlah faktor yang tepat TETAPI kehilangan pemetaan.
musim dingin
11
@Thierry - di tangan saya, menggunakan ggplot2_0.9.3.1, metode ini tidak berfungsi (lebih lama?); warna yang diberikan pada fCategory berbeda antara kedua plot. Namun, dengan senang hati, @wintour, saya pikir bahwa @hadley yang menyatakan bahwa + scale_colour_discrete(drop=TRUE,limits = levels(dataset$fCategory))untuk melestarikan warna | asosiasi faktor tetapi, yang bekerja, kecuali, di tangan saya, penurunan = TRUE adalah TIDAK dihormati (saya berharap untuk menghapus tingkat dari legenda). Drat ... atau ini aku?
Malcook
1
@malcook, alih-alih drop = TRUE, Anda harus menentukan level mana yang ingin Anda pertahankan melalui "break": github.com/hadley/ggplot2/issues/1433
Eric
17

Ini adalah pos lama, tetapi saya mencari jawaban untuk pertanyaan yang sama ini,

Mengapa tidak mencoba sesuatu seperti:

scale_color_manual(values = c("foo" = "#999999", "bar" = "#E69F00"))

Jika Anda memiliki nilai kategorikal, saya tidak melihat alasan mengapa ini tidak berhasil.

Pavlos Panteliadis
sumber
3
Ini sebenarnya apa jawaban Joran, tetapi gunakan myColors <- brewer.pal(5,"Set1"); names(myColors) <- levels(dat$grp)untuk menghindari harus secara manual kode level.
Axeman
Namun, jawaban Joran tidak menyulitkan nilai-nilai warna. Ada kasus di mana Anda memerlukan nilai warna tertentu untuk faktor tertentu.
René Nyffenegger
Sementara saya mendapatkan kelemahan dari "hard coding" dalam kasus-kasus tertentu, saya berpikir bahwa terlalu sering lapisan pengembang abstraksi / menambahkan coders membuat pekerjaan mereka kurang dapat diakses, tidak lebih. Maksudnya 100% jelas dalam kasus ini. Plus cukup mudah untuk memikirkan bagaimana membuat fungsi utilitas yang memperluas contoh ini yang mengembalikan vektor bernama warna tertentu.
Matt Barstead
16

Berdasarkan jawaban yang sangat membantu oleh joran, saya dapat menemukan solusi ini untuk skala warna yang stabil untuk faktor boolean ( TRUE, FALSE).

boolColors <- as.character(c("TRUE"="#5aae61", "FALSE"="#7b3294"))
boolScale <- scale_colour_manual(name="myboolean", values=boolColors)

ggplot(myDataFrame, aes(date, duration)) + 
  geom_point(aes(colour = myboolean)) +
  boolScale

Karena ColorBrewer tidak terlalu membantu dengan skala warna biner, dua warna yang diperlukan ditentukan secara manual.

Berikut mybooleanadalah nama kolom dalam myDataFramememegang faktor BENAR / SALAH. datedan durationapakah nama kolom yang akan dipetakan ke sumbu x dan y plot dalam contoh ini.

Marian
sumber
Pendekatan lain adalah menerapkan "as.character ()" ke kolom. Ini akan membuatnya menjadi kolom string yang berfungsi baik dengan skala _ * _ manual
Sahir Moosvi