r (stats) 中用于聚类的 dist 函数:我应该将我的 ID 变量放在 row.names 中吗?
dist function in r (stats) for clustering: Should I put my ID variable in row.names?
我有一个包含一些数字列和一个字符 ID 列的数据框。当我在 dist 函数中传递整个数据帧时,它会计算距离矩阵,但是当我删除 ID 列并将其传递给距离函数时,我不会得到相同的结果。
1) 为什么会出现这种奇怪的行为?
2) 在 R 中如何处理 "ID" 列?我应该删除 ID 列还是应该将它们放入 row.names.
PS 我通常使用 tibbles 和 tidyverse 中的工具。
当我们将包含 factor/character 个变量的数据帧传递给 dist
时会发生什么并不明显。
首先,如果是数字数据的字符,比如c("1", "2")
,那么它会被强制转换回数字数据。在那种情况下,除非 ID 之间的差异有意义,否则您显然不应包含此变量。
现在让我们考虑一个问题,如果我们有一个不是上述类型的字符因子会发生什么。在 C source code 中,我们找到了一些重要的行:
static double R_euclidean(double *x, int nr, int nc, int i1, int i2)
{
double dev, dist;
int count, j;
count= 0;
dist = 0;
for(j = 0 ; j < nc ; j++) {
if(both_non_NA(x[i1], x[i2])) {
dev = (x[i1] - x[i2]);
if(!ISNAN(dev)) {
dist += dev * dev;
count++;
}
}
i1 += nr;
i2 += nr;
}
if(count == 0) return NA_REAL;
if(count != nc) dist /= ((double)count/nc);
return sqrt(dist);
}
首先(不在此函数中),factor/character 变量在尝试将它们转换为整数时被强制转换为 NA。 (警告消息也这么说。)结果,正如我们在 R_euclidean
的代码中看到的,我们进行了一些重新缩放:
if(count != nc) dist /= ((double)count/nc);
return sqrt(dist);
其中 nc
是总列数,count
是数值列数。我们可以验证这一点:
k <- 20
df <- data.frame(a = sample(letters, k, replace = TRUE),
b = sample(letters, k, replace = TRUE),
c = rnorm(k), d = rnorm(k))
max(abs(as.matrix(dist(df)) * sqrt(2 / ncol(df)) - as.matrix(dist(df[, 3:4]))))
# [1] 7.467696e-09
也就是说,我们比较了 df
没有重新缩放(乘以 sqrt(2 / ncol(df))
)的距离矩阵和没有两个因子变量的距离矩阵。好像有一些数值上的错误但是矩阵基本一样
因此,这解释了为什么结果不同。如果您打算使用单个矩阵进行聚类,则保留 factors/characters 似乎没问题,因为比例无关紧要。但是,在比例很重要的情况下,您应该先删除 factor/character 列。 (是将您的 ID 变量用作行名称还是用作单独的向量并不重要,这取决于您。)
我有一个包含一些数字列和一个字符 ID 列的数据框。当我在 dist 函数中传递整个数据帧时,它会计算距离矩阵,但是当我删除 ID 列并将其传递给距离函数时,我不会得到相同的结果。
1) 为什么会出现这种奇怪的行为?
2) 在 R 中如何处理 "ID" 列?我应该删除 ID 列还是应该将它们放入 row.names.
PS 我通常使用 tibbles 和 tidyverse 中的工具。
当我们将包含 factor/character 个变量的数据帧传递给 dist
时会发生什么并不明显。
首先,如果是数字数据的字符,比如c("1", "2")
,那么它会被强制转换回数字数据。在那种情况下,除非 ID 之间的差异有意义,否则您显然不应包含此变量。
现在让我们考虑一个问题,如果我们有一个不是上述类型的字符因子会发生什么。在 C source code 中,我们找到了一些重要的行:
static double R_euclidean(double *x, int nr, int nc, int i1, int i2)
{
double dev, dist;
int count, j;
count= 0;
dist = 0;
for(j = 0 ; j < nc ; j++) {
if(both_non_NA(x[i1], x[i2])) {
dev = (x[i1] - x[i2]);
if(!ISNAN(dev)) {
dist += dev * dev;
count++;
}
}
i1 += nr;
i2 += nr;
}
if(count == 0) return NA_REAL;
if(count != nc) dist /= ((double)count/nc);
return sqrt(dist);
}
首先(不在此函数中),factor/character 变量在尝试将它们转换为整数时被强制转换为 NA。 (警告消息也这么说。)结果,正如我们在 R_euclidean
的代码中看到的,我们进行了一些重新缩放:
if(count != nc) dist /= ((double)count/nc);
return sqrt(dist);
其中 nc
是总列数,count
是数值列数。我们可以验证这一点:
k <- 20
df <- data.frame(a = sample(letters, k, replace = TRUE),
b = sample(letters, k, replace = TRUE),
c = rnorm(k), d = rnorm(k))
max(abs(as.matrix(dist(df)) * sqrt(2 / ncol(df)) - as.matrix(dist(df[, 3:4]))))
# [1] 7.467696e-09
也就是说,我们比较了 df
没有重新缩放(乘以 sqrt(2 / ncol(df))
)的距离矩阵和没有两个因子变量的距离矩阵。好像有一些数值上的错误但是矩阵基本一样
因此,这解释了为什么结果不同。如果您打算使用单个矩阵进行聚类,则保留 factors/characters 似乎没问题,因为比例无关紧要。但是,在比例很重要的情况下,您应该先删除 factor/character 列。 (是将您的 ID 变量用作行名称还是用作单独的向量并不重要,这取决于您。)