R 플롯 팁 2
빅데이터 플롯과 알파
데이터의 크기가 매우 클 때, 기본 산점도는 여러 점에 겹치기 때문에 알파를 조정하는 방법을 많이 쓴다.
library(data.table)
library(ggplot2)
library(dplyr)
#N <- 1000
N <- 1000000
x <- rnorm(N)
y <- x + rnorm(N)
dat <- data.table(x = x,
y = y)
dat %>% ggplot(aes(x=x, y=y)) + geom_point()
dat %>% ggplot(aes(x=x, y=y)) + geom_point(alpha=0.01)
ggplot2의 알파 최소값은 0.01
인 듯 하다. 따라서 데이터의 크기가 매우 클 때에는 alpha=0.01
도 충분히 작지 않은 경우가 있다. (위의 플롯을 보면 중앙의 검은 색은 하나의 큰 덩어리로 보인다. 이것은 밀도가 거의 비슷한 부분일 수도 있고, 농도가 더 짙어질 수 없기 때문일 수도 있다. alpha=1
에서 서로 농도가 분간되지 않는 부분이 큰 것과 같은 이유이다.)
이변량 정규분포는 너무 단순하므로 충분히 복잡한 데이터를 사용해보자.
#N <- 100000
#x <- rnorm(N)
#y <- 2*exp(x/10) + 2*abs(x)*sin(x) + rt(N, df = 30)
x1 <- rnorm(N/2)
y1 <- 2*sin(x1) + rnorm(N/2)
x2 <- rnorm(N/2)
y2 <- 2*cos(x2) + rt(N/2, df=30)
dat <- data.table(x=c(x1,x2),
y=c(y1,y2))
여러 알파값
이에 대한 대안은 여러 알파를 사용하여 산점도를 그리는 것이다.
p1 <- dat %>% ggplot(aes(x=x, y=y)) + geom_point() +
labs(title='alpha=1') + theme_minimal()
p2 <- dat %>% ggplot(aes(x=x, y=y)) + geom_point(alpha=0.1) +
labs(title='alpha=0.1') + theme_minimal()
p3 <- dat %>% ggplot(aes(x=x, y=y)) + geom_point(alpha=0.05) +
labs(title='alpha=0.05') + theme_minimal()
p4 <- dat %>% ggplot(aes(x=x, y=y)) + geom_point(alpha=0.01) +
labs(title='alpha=0.01') + theme_minimal()
library(cowplot)
## ## ********************************************************
## Note: As of version 1.0.0, cowplot does not change the
## default ggplot2 theme anymore. To recover the previous
## behavior, execute: ## theme_set(theme_cowplot())
## ********************************************************
plot_grid(p1,p2,p3,p4,ncol=2)
하지만 알파의 최저값 alpha=0.01
를 써도 중심부의 밀도 차이가 자세히 드러나지 않는다. 이런 경우 샘플링을 활용할 수 있다.
샘플링
p1 <- dat %>% sample_n(N/5) %>%
ggplot(aes(x=x, y=y)) + geom_point(alpha=0.01) +
labs(title='alpha=0.01 with 20% of data') + theme_minimal()
p2 <- dat %>% sample_n(N/10) %>%
ggplot(aes(x=x, y=y)) + geom_point(alpha=0.01) +
labs(title='alpha=0.01 with 10% of data') + theme_minimal()
p3 <- dat %>% sample_n(N/50) %>%
ggplot(aes(x=x, y=y)) + geom_point(alpha=0.01) +
labs(title='alpha=0.01 with 2% of data') + theme_minimal()
p4 <- dat %>% sample_n(N/100) %>%
ggplot(aes(x=x, y=y)) + geom_point(alpha=0.01) +
labs(title='alpha=0.01 with 1% of data') + theme_minimal()
library(cowplot)
plot_grid(p1,p2,p3,p4,ncol=2)
하지만 샘플링은 전체 데이터의 일부만을 사용하고 있고, 무작위로 샘플을 뽑고 있기 때문에 데이터의 밀도를 정확하게 나타내지 못한다는 단점이 있다.
조건부 분포
산점도를 그려보는 이유는 여러 가지가 있다. 한 가지 이유는 선형회귀분석, ML, DL 등을 통해 회귀 분석을 하기 전에 적합한 모형을 확인하기 위해 EDA를 하는 것이다.
이때 중요한 것은 이변량 분포보다는 조건부 분포, 즉 \(\mathbb{p}(y|x)\) 를 확인하는 것이다. 이에 반해, 위의 모든 플롯은 이변량 분포에 촛점을 맞추고 있다.
만약 \(x\) 조건부 대표값을 확인하고자 한다면 다음과 비모수 회귀선을 그려볼 수 있다.
회귀선
p1 <- dat %>% sample_n(N/100) %>%
ggplot(aes(x=x, y=y)) + geom_point(alpha=0.01) +
geom_smooth(method='loess') +
labs(title='alpha=0.01 with 1% of data, loess') + theme_minimal()
p2 <- dat %>% sample_n(N/100) %>%
ggplot(aes(x=x, y=y)) + geom_point(alpha=0.01) +
geom_smooth(method='auto') +
labs(title='alpha=0.01 with 1% of data, gam') + theme_minimal()
print(p1)
print(p2)
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'
이 방법을 통해 조건부 평균( \(\mathbb{E}[y|x] = \int y\ \mathbb{p}(y|x) dy\) )을 확인할 수 있지만, 조건부 분포까지 확인하기는 어렵다.
조건부 분포
\(x\) 구획
앞의 예에서 봤듯이 일괄적으로 알파를 조정하는 방법은 데이터가 밀집되어 있는 곳에서는 여전히 농도가 최대값이 되어 밀도를 구분할 수 없으며, 데이터가 듬성한 곳에서는 점이 너무 흐려서 데이터를 확인할 수 없다. 한 가지 방법은 \(x\) 를 구간별로 구획해서 적절히 샘플링하는 것이다. 다음과 같다.
dat %>%
mutate(xCut = cut(x, breaks=10)) %>%
group_by(xCut) %>%
do(sample_n(., 10000, replace=TRUE)) %>%
ggplot(aes(x=x, y=y)) +
geom_point(alpha=0.01)
하지만 데이터의 밀도가 \(x\) 방향으로 물결치는 듯이 보인다. 이는 구획 구간이 넓어서 생기는 부작용이다. 구획 구간을 줄인다면 이런 무늬는 잘 보이지 않는다.
dat %>%
mutate(xCut = cut(x, breaks=50)) %>%
group_by(xCut) %>%
do(sample_n(., 2000, replace=TRUE)) %>%
ggplot(aes(x=x, y=y)) +
geom_point(alpha=0.01)
\(x\) 밀도 추정
\(x\) 에 인의적인 구획을 하기 싫다면, \(x\) 의 밀도를 추정할 수도 있을 것이다. 그리고
xDensity <- ks::kde(dat$x)
dat$prob <- predict(xDensity, x = dat$x)
#head(dat)
dat %>%
mutate(xCut = cut(x, breaks=50)) %>%
group_by(xCut) %>%
do(sample_n(., 2000, replace=TRUE, weight=1/prob)) %>%
ggplot(aes(x=x, y=y)) +
geom_point(alpha=0.01)
\(x\) 분포 감안
마지막으로 모든 \(x\) 를 동일하게 취급하는 것이 문제라고 생각한다면, \(x\) 의 밀도를 약간 감안하여(모두 감안한다면 산점도와 동일하게 되므로, 밀도에 \(\log\) 를 취하던지 해서) 산점도를 그려볼 수 있을 것이다.
Leave a comment