R 三维数据绘图

如何使用 R 直观地可视化三维数据?

Posted by Paradise on April 12, 2021

在做机器学习和统计分析经常会有一些维度较高的数据。三维数据还可以在坐标系中表示出来,大于三维的时候只能降维或者从统计量上来理解数据了。

虽然三维数据还是可以较直观地可视化的,但是想在二维的图中呈现三维的数据,就像向普通人描述四维空间一样,是较难理解的。所以在这里研究一下如何进行三维数据的可视化。

绘图测试

按照惯例,在 R 中,拿到任何数据都 plot 一下。如果结果可以清晰明了地显示出来,基本上问题解决了。

1
2
3
4
5
library(tidyverse)

data <- read.csv('math.csv')
# 这个数据从硬盘垃圾堆捡来的,也不知道有什么,plot一下
plot(data)

可以看到实际上 plot 函数调用了 pairs 函数,结果与 pairs 的绘图结果一样。从这个图可以看到里面有 8 条相交于原点的线段,每条线段都不与任何坐标轴构成的平面平行,并且 8 条直线存在一定的对称关系。但是并不能直观地感受到每条直线在空间中的位置关系。

接下来可以考虑其他表示三个维度的信息的图表,例如:带有分组的条形图、带有颜色变量的散点图折线图、热力图、网格图,等。这里试一下网格图:

1
2
# 使用 ggplot2 提供的 hexmap
data %>% ggplot(aes(a, b)) + geom_hex(bins=100)

这个图中的颜色深度表示的是对应网格内的频数。因此图中表达的信息是:在 a-b 平面的投影中,中间的点比较密集,并且中间两条较短的线段与 a-b 平面的夹角较大。这个图的信息维度的确有三维,但是它只使用了原始数据中 a、b 两个变量,第三个维度是用 a、b 的统计量(众数)生成的。其它表示三个维度信息的图也是类似的,很难用于可视化三维的数据。

上个方案不合理,下面试一下降维来表示,将三维空间的图像分别“拍扁”到三个坐标平面,再靠我们“想象力”将它转换成三维图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 使用 ggplot2 中的 geom_point 画二维散点图
p1<-data%>%ggplot(aes(a,b))
p2<-data%>%ggplot(aes(b,c))
p3<-data%>%ggplot(aes(c,a))

# 为了更清楚地显示各线段的相对位置关系,这里以原点为中心画上单位圆辅助线
add_unit_circle<-function(p){

    # 调整样式
    p <- p + geom_point(aes(color='blue')) + coord_fixed()

    # 添加单个单位圆的函数
    unit_circle <- function(n,p){
        N = n*100
        x = seq(-N, N, by = 1)
        y1 = x*0
        y2 = x*0
        for (i in seq(1, length(x))){
            y1[i] = -sqrt(N^2 - x[i]^2)
            y2[i] = sqrt(N^2 - x[i]^2)
            if(i > N){
                y1[i] = sqrt(N^2 - x[i]^2)
                y2[i] = -sqrt(N^2 - x[i]^2)
            }
        }
        df <- tibble(x=x, y1=y1, y2=y2)
        p <- p + geom_point(aes(x, y1, color='green'), data=df)
        p <- p + geom_point(aes(x, y2, color='green'), data=df)
        return(p)
    }
    
    # 添加 10 个单位圆
    for (i in 1:10) {
        p <- unit_circle(i, p) + theme(legend.position='None')
    }
    
    return(p)
}

for p in c(p1, p2, p3){
    add_unit_circle(p)
}

这段代码有点臃肿,实际上实现的功能很简单,就是在原图上加几个圆圈。这里懒得重构了,结果如下。三个图分别对应“俯视图”、“侧视图”以及“正视图”。效果比直接用 pairs 函数画出来的好一点。

总得来说使用二维绘图函数基本可以描述数据的概况,但是很难做到足够直观,下面试一下三维绘图函数。在三维坐标系上绘图。

1
2
3
4
library(scatterplot3d)

p1 <- scatterplot3d(data, pch=20, highlight.3d=TRUE, angle=30, type='h')
p2 <- scatterplot3d(data, pch=20, highlight.3d=TRUE, angle=60, type='h')

通过调整角度,优化参数,可以使图像更加直观。

总结

尝试了几种可视化思路,结果都一般般。三维图像不容易实现直观的可视化,有空继续研究更新。