Jak mogę drukować za pomocą 2 różnych osi Y?

122

Chciałbym nałożyć dwa wykresy rozrzutu w R, tak aby każdy zestaw punktów miał swoją własną (inną) oś Y (tj. W pozycjach 2 i 4 na rysunku), ale punkty wydają się nałożone na tej samej figurze.

Czy można to zrobić plot?

Edytuj przykładowy kod pokazujący problem

# example code for SO question
y1 <- rnorm(10, 100, 20)
y2 <- rnorm(10, 1, 1)
x <- 1:10
# in this plot y2 is plotted on what is clearly an inappropriate scale
plot(y1 ~ x, ylim = c(-1, 150))
points(y2 ~ x, pch = 2)
DQdlM
źródło
Proszę podać przykładowe dane. To generalnie zły pomysł z estetycznego punktu widzenia.
Chase
3
odpowiedzi i dyskusja w konkretnym przypadku ggplot2: stackoverflow.com/questions/3099219/… (szukam SO [r] two y-axeslub [r] twoord.plot) - jest kilka innych powiązanych odpowiedzi, chociaż (ku mojemu zdziwieniu, ponieważ jest to R FAQ) nic identycznego
Ben Bolker
@chase - dodałem działający przykład problemu. Dziękuję za ostrzeżenie w kwestiach estetycznych.
DQdlM

Odpowiedzi:

126

aktualizacja : skopiowany materiał, który znajdował się na wiki R pod adresem http://rwiki.sciviews.org/doku.php?id=tips:graphics-base:2yaxes , łącze jest teraz uszkodzone: również dostępne z wayback machine

Dwie różne osie y na tej samej działce

(trochę materiału oryginalnie autorstwa Daniela Rajdla 2006/03/31 15:26)

Należy pamiętać, że jest bardzo niewiele sytuacji, w których właściwe jest użycie dwóch różnych skal na tym samym poletku. Bardzo łatwo jest wprowadzić w błąd widza grafiki. Sprawdź następujące dwa przykłady i uwagi dotyczące tej kwestii ( Przykład 1 , Przykład 2 z Junk Charts ), a także ten artykuł Stephena Few (który stwierdza „Na pewno nie można stwierdzić, raz na zawsze, że wykresy z podwójną skalę osi nie są przydatne; tylko tyle, że nie potrafię wymyślić sytuacji, która je uzasadnia w świetle innych, lepszych rozwiązań. ”) Zobacz również punkt 4 na tej kreskówce ...

Jeśli jesteś zdeterminowany, podstawową receptą jest utworzenie pierwszego wykresu, ustawienie par(new=TRUE)uniemożliwiające R wyczyszczenie urządzenia graficznego, utworzenie drugiego wykresu z axes=FALSE(i ustawienie xlabi ylabpozostawienie pustego - ann=FALSEpowinno również działać), a następnie użycie axis(side=4)do dodania nowej osi po prawej stronie i mtext(...,side=4)aby dodać etykietę osi po prawej stronie. Oto przykład wykorzystujący trochę zmyślonych danych:

set.seed(101)
x <- 1:10
y <- rnorm(10)
## second data set on a very different scale
z <- runif(10, min=1000, max=10000) 
par(mar = c(5, 4, 4, 4) + 0.3)  # Leave space for z axis
plot(x, y) # first plot
par(new = TRUE)
plot(x, z, type = "l", axes = FALSE, bty = "n", xlab = "", ylab = "")
axis(side=4, at = pretty(range(z)))
mtext("z", side=4, line=3)

twoord.plot()w plotrixpakiecie automatyzuje ten proces, podobnie jak doubleYScale()wlatticeExtra pakiecie.

Inny przykład (zaczerpnięty z postu na liście mailingowej R autorstwa Roberta W. Baera):

## set up some fake test data
time <- seq(0,72,12)
betagal.abs <- c(0.05,0.18,0.25,0.31,0.32,0.34,0.35)
cell.density <- c(0,1000,2000,3000,4000,5000,6000)

## add extra space to right margin of plot within frame
par(mar=c(5, 4, 4, 6) + 0.1)

## Plot first set of data and draw its axis
plot(time, betagal.abs, pch=16, axes=FALSE, ylim=c(0,1), xlab="", ylab="", 
   type="b",col="black", main="Mike's test data")
axis(2, ylim=c(0,1),col="black",las=1)  ## las=1 makes horizontal labels
mtext("Beta Gal Absorbance",side=2,line=2.5)
box()

## Allow a second plot on the same graph
par(new=TRUE)

## Plot the second plot and put axis scale on right
plot(time, cell.density, pch=15,  xlab="", ylab="", ylim=c(0,7000), 
    axes=FALSE, type="b", col="red")
## a little farther out (line=4) to make room for labels
mtext("Cell Density",side=4,col="red",line=4) 
axis(4, ylim=c(0,7000), col="red",col.axis="red",las=1)

## Draw the time axis
axis(1,pretty(range(time),10))
mtext("Time (Hours)",side=1,col="black",line=2.5)  

## Add Legend
legend("topleft",legend=c("Beta Gal","Cell Density"),
  text.col=c("black","red"),pch=c(16,15),col=c("black","red"))

wprowadź opis obrazu tutaj

Podobnych receptur można użyć do nałożenia wykresów różnych typów - wykresów słupkowych, histogramów itp.

Ben Bolker
źródło
dlatego odpowiedzi zawierające tylko linki są złym pomysłem ... wiki.r-project.org wygląda na zlikwidowane, pytam na [email protected].
Ben Bolker,
@BenBolker Twoje rozwiązanie jest genialne! jednak mam jedno pytanie. Jeśli jest więcej niż jedna linia po obu stronach, nadal mogę używać tej metody, ale tylko z symbolami. Kiedy próbuję użyć line = plot, próbuje je wykreślić w sposób ciągły. Czy zasugerowałbyś sztuczkę, aby to naprawić?
wthimdh
1
@BenBolker Co jeśli Czas jest formatem daty typu: „2019-01-01”. Jak zmienisz axis(1,pretty(range(time),10))linię?
k.dkhk
35

Jak sama nazwa wskazuje, twoord.plot()w pakiecie plotrix kreśli dwie osie rzędnych.

library(plotrix)
example(twoord.plot)

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

kmm
źródło
3
przysiek. dzięki za ładunek ciężarówek przykładów. Bardzo chciałbym zobaczyć jeden z tych przykładów linii i słupków z wartościami ujemnymi. Przydałoby się również układanie w stosy.
Matt Bannert,
5

Jedną z opcji jest wykonanie dwóch działek obok siebie. ggplot2zapewnia fajną opcję z facet_wrap():

dat <- data.frame(x = c(rnorm(100), rnorm(100, 10, 2))
  , y = c(rnorm(100), rlnorm(100, 9, 2))
  , index = rep(1:2, each = 100)
  )

require(ggplot2)
ggplot(dat, aes(x,y)) + 
geom_point() + 
facet_wrap(~ index, scales = "free_y")
Pościg
źródło
4

Jeśli możesz zrezygnować ze skal / etykiet osi, możesz przeskalować dane do (0, 1) interwału. Działa to na przykład w przypadku różnych śladów „poruszania się” na chromosomach, gdy interesują Cię lokalne korelacje między ścieżkami i mają one różne skale (pokrycie w tysiącach, Fst 0-1).

# rescale numeric vector into (0, 1) interval
# clip everything outside the range 
rescale <- function(vec, lims=range(vec), clip=c(0, 1)) {
  # find the coeficients of transforming linear equation
  # that maps the lims range to (0, 1)
  slope <- (1 - 0) / (lims[2] - lims[1])
  intercept <- - slope * lims[1]

  xformed <- slope * vec + intercept

  # do the clipping
  xformed[xformed < 0] <- clip[1]
  xformed[xformed > 1] <- clip[2]

  xformed
}

Następnie, mając ramkę danych z chrom,position , coveragei fstkolumn, można zrobić coś takiego:

ggplot(d, aes(position)) + 
  geom_line(aes(y = rescale(fst))) + 
  geom_line(aes(y = rescale(coverage))) +
  facet_wrap(~chrom)

Zaletą tego jest to, że nie jesteś ograniczony do dwóch traków.

liborm
źródło
4

Ja również sugeruję, twoord.stackplot()w plotrixpakiecie działki z większą liczbą dwóch osi rzędnych.

data<-read.table(text=
"e0AL fxAL e0CO fxCO e0BR fxBR anos
 51.8  5.9 50.6  6.8 51.0  6.2 1955
 54.7  5.9 55.2  6.8 53.5  6.2 1960
 57.1  6.0 57.9  6.8 55.9  6.2 1965
 59.1  5.6 60.1  6.2 57.9  5.4 1970
 61.2  5.1 61.8  5.0 59.8  4.7 1975
 63.4  4.5 64.0  4.3 61.8  4.3 1980
 65.4  3.9 66.9  3.7 63.5  3.8 1985
 67.3  3.4 68.0  3.2 65.5  3.1 1990
 69.1  3.0 68.7  3.0 67.5  2.6 1995
 70.9  2.8 70.3  2.8 69.5  2.5 2000
 72.4  2.5 71.7  2.6 71.1  2.3 2005
 73.3  2.3 72.9  2.5 72.1  1.9 2010
 74.3  2.2 73.8  2.4 73.2  1.8 2015
 75.2  2.0 74.6  2.3 74.2  1.7 2020
 76.0  2.0 75.4  2.2 75.2  1.6 2025
 76.8  1.9 76.2  2.1 76.1  1.6 2030
 77.6  1.9 76.9  2.1 77.1  1.6 2035
 78.4  1.9 77.6  2.0 77.9  1.7 2040
 79.1  1.8 78.3  1.9 78.7  1.7 2045
 79.8  1.8 79.0  1.9 79.5  1.7 2050
 80.5  1.8 79.7  1.9 80.3  1.7 2055
 81.1  1.8 80.3  1.8 80.9  1.8 2060
 81.7  1.8 80.9  1.8 81.6  1.8 2065
 82.3  1.8 81.4  1.8 82.2  1.8 2070
 82.8  1.8 82.0  1.7 82.8  1.8 2075
 83.3  1.8 82.5  1.7 83.4  1.9 2080
 83.8  1.8 83.0  1.7 83.9  1.9 2085
 84.3  1.9 83.5  1.8 84.4  1.9 2090
 84.7  1.9 83.9  1.8 84.9  1.9 2095
 85.1  1.9 84.3  1.8 85.4  1.9 2100", header=T)

require(plotrix)
twoord.stackplot(lx=data$anos, rx=data$anos, 
                 ldata=cbind(data$e0AL, data$e0BR, data$e0CO),
                 rdata=cbind(data$fxAL, data$fxBR, data$fxCO),
                 lcol=c("black","red", "blue"),
                 rcol=c("black","red", "blue"), 
                 ltype=c("l","o","b"),
                 rtype=c("l","o","b"), 
                 lylab="Años de Vida", rylab="Hijos x Mujer", 
                 xlab="Tiempo",
                 main="Mortalidad/Fecundidad:1950–2100",
                 border="grey80")
legend("bottomright", c(paste("Proy:", 
                      c("A. Latina", "Brasil", "Colombia"))), cex=1,
        col=c("black","red", "blue"), lwd=2, bty="n",  
        lty=c(1,1,2), pch=c(NA,1,1) )
Juannes
źródło
1

Inną alternatywą, podobną do zaakceptowanej odpowiedzi przez @BenBolker, jest ponowne zdefiniowanie współrzędnych istniejącego wykresu podczas dodawania drugiego zestawu punktów.

Oto minimalny przykład.

Dane:

x  <- 1:10
y1 <- rnorm(10, 100, 20)
y2 <- rnorm(10, 1, 1)

Wątek:

par(mar=c(5,5,5,5)+0.1, las=1)

plot.new()
plot.window(xlim=range(x), ylim=range(y1))
points(x, y1, col="red", pch=19)
axis(1)
axis(2, col.axis="red")
box()

plot.window(xlim=range(x), ylim=range(y2))
points(x, y2, col="limegreen", pch=19)
axis(4, col.axis="limegreen")

przykład

Karolis Koncevičius
źródło