【问题标题】:Loop on a data frame with considering the types of the values在考虑值的类型的情况下循环数据帧
【发布时间】:2018-09-29 17:53:32
【问题描述】:

我有一个大的坐标数据集,我想计算它们之间的欧几里得距离。示例如下:

df <- data.frame("name" = c("a","b","c","a","e"), "type" = c("me","me","me","we", "we"), 
    "x" = c(64.044,63.722,64.359,65.373, 65.122),"y" = c(51.615,52.849,53.119,51.805,52.78),
    "z" = c(33.423,32.671,31.662,34.158,35.26))
name  type    x   y   z
a me  64.044  51.615  33.423
b me  63.722  52.849  32.671
c me  64.359  53.119  31.662
d we  65.373  51.805  34.158
e we  65.122  52.78   35.26

我想计算具有不同类型的名称列值的行之间的欧几里得距离值。我写了这段代码

require("distances")

for (i in 1:nrow(df)) {
  if(!(df$type[i]%in%df$type[i+1])){
    d <- distances(df[,3:5])
  }
}

然而这是错误的。我的理想结果应该如下所示,其中 d 说明了名称值之间的距离:

    a.me    b.me    c.me    a.we    e.we
a.me    0   0   0   d   d
b.me    0   0   0   d   d
c.me    0   0   0   d   d
a.we    d   d   d   0   0
e.we    d   d   d   0   0

此外,我的数据集变化很大,我认为 for 不是最佳选择。任何人都可以在这个问题上帮助我吗?

【问题讨论】:

  • 这是你想要的吗:dist(df[3:5], diag = TRUE, upper = TRUE)
  • @RuiBarradas 不,我使用了“距离”包中的距离函数。

标签: r for-loop dataframe euclidean-distance


【解决方案1】:

这是一个解决方案,但根据您的数据大小可能需要大量计算工作,因为首先计算整个 dist-matrix,然后只选择需要的那些:

dist_mat <- dist(df[3:5], diag = TRUE, upper = TRUE)
dist_mat <- as.matrix(dist_mat)
names_vec <- paste(df$name, df$type, sep = "_")
dimnames(dist_mat) <- list(names_vec, names_vec)

dist_mat <- 
  sapply(colnames(dist_mat), 
         function(x) ifelse(grepl(strsplit(x, "_")[[1]][2], 
                                  row.names(dist_mat)), NA, dist_mat[,x]))

row.names(dist_mat) <- names_vec

dist_mat
#          a_me     b_me     c_me     d_we     e_we
# a_me       NA       NA       NA 1.530544 2.427731
# b_me       NA       NA       NA 2.454976 2.944093
# c_me       NA       NA       NA 2.997467 3.693602
# d_we 1.530544 2.454976 2.997467       NA       NA
# e_we 2.427731 2.944093 3.693602       NA       NA

【讨论】:

  • 感谢您的回复。很好,但正如你所说,这很耗时。
【解决方案2】:

这就是诀窍:

  df <- data.frame("name" = c("a","b","c","d","e"), "type" = c("me","me","me","we", "we"), 
                   "x" = c(64.044,63.722,64.359,65.373, 65.122),"y" = c(51.615,52.849,53.119,51.805,52.78),
                   "z" = c(33.423,32.671,31.662,34.158,35.26))

# lapply over the levels of the type
x <- lapply(levels(df$type), function(level) {
  # select the matching rows and columns and convert
  mat <- as.matrix(df[as.character(df$type) == level, 3:5])
  # names are set as row names for dist to use
  row.names(mat) <- paste(df$name[as.character(df$type) == level],
                          level,
                          sep = "_")
  # measuring the distance
  dist <- dist(mat, method = "euclidean", diag = TRUE, upper = TRUE)
  # converting distance to matrix
  as.matrix(dist)
})
# bind the list to one matrix
x <- plyr::rbind.fill.matrix(x)
# add rownames
row.names(x) <- colnames(x)
x

a_me     b_me     c_me     d_we     e_we
a_me 0.000000 1.480522 2.337170       NA       NA
b_me 1.480522 0.000000 1.223417       NA       NA
c_me 2.337170 1.223417 0.000000       NA       NA
d_we       NA       NA       NA 0.000000 1.492659
e_we       NA       NA       NA 1.492659 0.000000

【讨论】:

  • 非常感谢您的回复,但它对我的问题没有用。我想因为我的名字列中有重复的值。我会改进我的例子来更好地解释这个问题。
  • 换句话说,我需要每个组的成员之间的欧几里得距离作为关于一个成员可以存在于多个组中的类型。
  • 我认为在这种情况下最好提出一个新问题。这就是您所描述的最佳结果。如果你有重复的列名,你应该用colnames(df) &lt;- make.names(colnames(df))之类的东西来修复它。
  • 列名不重复。我有重复的行名。正是在我改进的示例中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多