在 R 中重构 3D 图的数据框

Restructuring a data frame for 3D plots in R

我经常意识到 3D 绘图并不是呈现一组数据的最有效方式,但我之前为特定数据集制作的 2D 绘图似乎表明 3D 绘图有助于分解信息分成更不同的集群进行分析。话虽如此,我从来没有在 R 中这样做过,而且在使用 plot3d() 制作 3D 散点图之前,我无法重构我的数据框。

目前,我的数据框有 2 列和几千行信息。第一列是标识符,A、B、C...,第二列是该标识符的一个测量特征。

ID Area 
A   1.2
A   3.0
A   2.7
B   1.4
B   2.5
C   4.3
C   2.1
C   1.7

我将在 Y 轴上绘制区域。使用像 table() 这样的函数,我可以获得 A、B 或 C 出现的次数:(A=3,B=2,C=3) 并且这个值将成为所有的 x 坐标具有该结果的 ID。但我想做的是将该信息也放入第三列,为给定的 x 坐标分配一个唯一的 z。换句话说,Z 应该表示给定 X 出现了多少次,并且对于特定 X 的每个新实例都会增加 1。最终,原因是特定区域中所有对象的面积值 (y) ID 在唯一的 x,z 坐标上堆叠在一起。这就是我被困的地方。 本质上,我希望给定上述输入的最终数据帧输出如下所示:

ID(x) Area(y)  Z
    3    1.2   1
    3    3.0   1
    3    2.7   1
    2    1.4   1
    2    2.5   1
    3    4.3   2
    3    2.1   2
    3    1.7   2 

我们可以通过几种方式做到这一点。

1.基础 R - aggregate/ave

我们可以使用aggregate获取'ID'列中每个元素('IDx')的长度,通过创建'Z' 列基于 'IDx' 和 'merge' 中的重复元素 'dfN' 与原始数据集 'df1'

dfN <- aggregate(cbind(IDx=seq_along(ID))~ID, df1, FUN=length)
dfN$Z <- with(dfN, ave(IDx, IDx, FUN=function(x) cumsum(duplicated(x))+1L))
 merge(df1, dfN, by='ID')[-1]
 #  Area IDx Z
 #1  1.2   3 1
 #2  3.0   3 1
 #3  2.7   3 1
 #4  1.4   2 1
 #5  2.5   2 1
 #6  4.3   3 2
 #7  2.1   3 2
 #8  1.7   3 2

2。基础 R - ave/rle

我们可以使用 ave 创建 'IDx' 列,然后使用 `rle/inverse.rle' 创建 'Z' 列

 df1$IDx <- with(df1, ave(seq_along(ID), ID, FUN=length))
 v1 <- with(df1, paste0(ID, IDx))
 df1$Z <- inverse.rle(within.list(rle(v1), values <-ave(lengths, 
             lengths, FUN=function(x) cumsum(duplicated(x))+1L)))
 df1
 #  ID Area IDx Z
 #1  A  1.2   3 1
 #2  A  3.0   3 1
 #3  A  2.7   3 1
 #4  B  1.4   2 1
 #5  B  2.5   2 1
 #6  C  4.3   3 2
 #7  C  2.1   3 2
 #8  C  1.7   3 2

3。 data.table

将 'data.frame' 转换为 'data.table' (setDT),创建 'IDx' 即 nrows (.N),按 [=55= 分组].基于 'IDx' 中的重复元素,我们可以创建 'Z' 列。设置key为'ID'(setkey),join与'df1',将不需要的列赋值给NULL(ID:= NULL)

library(data.table)
setkey(setDT(df1)[, list(IDx=.N), by = ID][, IDx1:= IDx][,
     list(ID,Z=cumsum(duplicated(IDx1))+1L) , IDx], ID)[df1][, ID := NULL][]

#   IDx Z Area
#1:   3 1  1.2
#2:   3 1  3.0
#3:   3 1  2.7
#4:   2 1  1.4
#5:   2 1  2.5
#6:   3 2  4.3
#7:   3 2  2.1
#8:   3 2  1.7

4。 dplyr

思路同上。我们使用 left_join

而不是 'merge'
library(dplyr)
left_join(df1, 
            df1 %>% 
              group_by(ID) %>% 
              summarise(IDx=n()) %>% 
              group_by(IDx) %>%
              mutate(Z=cumsum(duplicated(IDx))+1L), by='ID') %>% 
              select(-ID)
 #  Area IDx Z
 #1  1.2   3 1
 #2  3.0   3 1
 #3  2.7   3 1
 #4  1.4   2 1
 #5  2.5   2 1
 #6  4.3   3 2
 #7  2.1   3 2
 #8  1.7   3 2

注意: 用另一个数据集测试了这个 'df2'

数据

df1 <- structure(list(ID = c("A", "A", "A", "B", "B", "C", "C", "C"), 
Area = c(1.2, 3, 2.7, 1.4, 2.5, 4.3, 2.1, 1.7)), .Names = c("ID", 
"Area"), class = "data.frame", row.names = c(NA, -8L))

df2 <-  structure(list(ID = c("A", "A", "A", "B", "B", "C", "C", "C", 
"D", "D", "D", "E", "E", "F"), Area = c(1.2, 3, 2.7, 1.4, 2.5, 
4.3, 2.1, 1.7, 1.2, 1.4, 2.1, 1.2, 1.5, 2.3)), .Names = c("ID", 
"Area"), class = "data.frame", row.names = c(NA, -14L))