【问题标题】:R: Calculate distance between consecutive points per group and group themR:计算每组连续点之间的距离并将它们分组
【发布时间】:2019-04-09 11:30:29
【问题描述】:

我很难用这个...所以我试图找到每组彼此接近的点,并进一步将它们分组。让我根据下面的示例数据为您解释一下:

  Group    X   Y  Z
1   110 3762 431 10
2   112 4950 880 10
3   113 5062 873 20
4   113 5225 874 30
5   113 5262 875 10
6   113 5300 874 20

structure(list(Group = c(110, 112, 113, 113, 113, 113), X = c(3762, 
4950, 5062, 5225, 5262, 5300), Y = c(431, 880, 873, 874, 875, 
874), Z = c(10, 10, 20, 30, 10, 20)), row.names = c(NA, -6L), class = "data.frame")

我们可以看到我们有分组列 Group, X & Y 列是我们的坐标和 Z当点被定义为“接近”(欧几里得距离

我尝试过的:

我已经使用这个函数成功地计算了点之间的欧几里得距离:

for(i in 1:nrow(test)) {
  if(i > 1 && test$Group[i] == test$Group[i-1]) {
    test$Distance[i] <- sqrt(((test$X[i] - test$X[i-1]) ^ 2) + ((test$Y[i] - test$Y[i-1]) ^ 2))
  } else {
    test$Distance[i] <- NA
  }
}

这给了我这个:

   Group    X   Y  Z  Distance
1   110 3762 431 10        NA
2   112 4950 880 10        NA
3   113 5062 873 20        NA
4   113 5225 874 30 163.00307
5   113 5262 875 10  37.01351
6   113 5300 874 20  38.01316

这里一切都变得复杂了,因为每个组的第一行都有 NA 等等......

我想要达到的目标:

我想找到每个组的距离不大于 100 (Distance Z 列)。所以手动完成:

 Group    Z  Grouped
1   110  10   no     
2   112  10   no     
3   113  20   no     
4   113  60   yes

感谢您的帮助!

【问题讨论】:

  • 所以,澄清一下,您想通过在比指定阈值更接近时添加它们的“Z”分数(组内)来合并点?现在,你的结果将取决于你的分数的顺序,我想这是你想要的吗?
  • 是的,这正是我所需要的(还有额外的一列表明积分已被分组)
  • 嗯,这有点棘手,因为排序可能会影响结果,你有一个排序算法吗?例如。在这个例子中,如果点 5 接近点 3 会怎样?
  • 好吧,在这种情况下,您可以假设行号对应于顺序(或者您可以创建新列,只是增加数字)。原始数据来自通过杆的长度(X 列)和宽度(Y 列)进行测量的机器 --> 它通过杆的长度移动
  • 但是当我想到它时,你可能是对的,它让它变得更加复杂!

标签: r


【解决方案1】:

这很困难。我不确定我是否已经完全弄清楚了。

#get data and libraries

library(tidyverse)

df <- read.table(text = "
Group    X   Y  Z  Distance
1   110 3762 431 10        NA
2   112 4950 880 10        NA
3   113 5062 873 20        NA
4   113 5225 874 30 163.00307
5   113 5262 875 10  37.01351
6   113 5300 874 20  38.01316", header = T, stringsAsFactors = F)
df %>%
  group_by(Group) %>%
  do(melt(outer(.$Distance, .$Distance, `-`))) %>%
  filter(between(value, -100, 0) | between(value, 0, 100)) %>% 
  distinct(Var1) %>%
  mutate(grouped = 1) %>%
  rename(row = Var1) -> rows

  df %>% 
    group_by(Group) %>% 
    mutate(row = row_number()) %>%
  left_join(rows, by = c("row", "Group")) %>%
    mutate(grouped = ifelse(is.na(grouped), "no", "yes")) %>%
    group_by(Group, grouped) %>%
    mutate(Z = ifelse(!is.na(grouped), sum(Z), Z)) %>%
    distinct(Group, Z, grouped)


# A tibble: 4 x 3
# Groups:   Group, grouped [4]
  Group     Z grouped
  <int> <int> <chr>  
1   110    10 no     
2   112    10 no     
3   113    20 no     
4   113    60 yes 

希望它是您正在寻找的东西,如果不是,它可能会给您一些新的想法。

更新:现在我希望能真正帮助您:

df %>%
  group_by(Group) %>%
  mutate(int1 = lead(Distance) < 100 | Distance < 100,
         int1 = replace(int1, is.na(int1), FALSE),
         int2 = rleid(int1),
         int2 = replace(int2, !int1 | is.na(int1), NA)) -> df2

  df2 %>%
  filter(int1) %>% 
    group_by(Group, int2) %>%
    summarise(Z = sum(Z),
              Grouped = "yes") %>% 
    select(Group, Z, Grouped) %>%
    bind_rows(df2 %>%
                filter(!int1) %>%
                mutate(Grouped = "no") %>%
                select(Group, Z, Grouped)) %>%
    arrange(Group)

# A tibble: 4 x 3
# Groups:   Group [3]
  Group     Z Grouped
  <int> <int> <chr>  
1   110    10 no     
2   112    10 no     
3   113    60 yes    
4   113    20 no 

【讨论】:

  • 您好,谢谢您的回答!它几乎运行良好...我刚刚发现了一个问题,它与outer 函数有关--> 密切关注有趣的参数...使用- 是不正确的,因为已经计算了连续行之间的距离每组
  • 你是对的,我正在解决我自己想象的问题。我已经在考虑另一种解决方案。也许我很快就会来。
  • 非常感谢您的帮助,非常感谢!
【解决方案2】:

我制定了一个小用例,可以帮助您入门。它是一种使用 for 循环和基于列向量的聚合的基本方法,您可以应用成对的函数向量进行聚合。

df <- read.table(text = "
Group    X   Y  Z  Distance
1   110 3762 431 10        NA
2   112 4950 880 10        NA
3   113 5062 873 20        NA
4   113 5225 874 30 163.00307
5   113 5262 875 10  37.01351
6   113 5300 874 20  38.01316
7   114 5300 874 30  NA
8   114 5300 874 20  38.01316", header = T, stringsAsFactors = F)

aggregateIt <- function(df = data, #data.frame
                        returnRaw = F, #to get the raw unaggregted df (only first case from column `grouped` by `subgroup` usable in this application)
                        colsToAgg = c("Z1", "Z2", "Z3"), #cols to aggregate
                        how = c("sum", "sum", "max")) #how to aggregate the columns, `Z1` by sum, `Z2` by sum and `Z3` by max
  {
  count <- 1L
  result <- vector("integer", nrow(df))
  grouped <- vector("character", nrow(df))
  for(i in seq_len(length(result)-1L)){
    if(df$Group[i] != df$Group[i+1L]) {
      result[i] <- count
      grouped[i] <- "no"
      count <- count + 1L
      if((i+1L) == length(result)) {
        result[i+1L] <- count
        grouped[i+1L] <- "no"
      }
    } else {
        if(df$Distance[i+1L] > 100L) {
          result[i] <- count
          grouped[i] <- "no"
          count <- count + 1L
          if((i+1L) == length(result)) {
            result[i+1L] <- count
            grouped[i+1L] <- "no"
          }
        } else {
          result[i] <- count
          grouped[i] <- "yes"
          if((i+1L) == length(result)) {
            result[i+1L] <- count
            grouped[i+1L] <- "yes"
          }
        }
    }
  }
  df <- within(df, {subgroup <- result; grouped <- grouped})
  if(returnRaw) return(df)
  A <- Reduce(function(a, b) merge(a, b, by = "subgroup"), 
         lapply(seq_along(how), function(x) aggregate(.~subgroup, df[, c(colsToAgg[x], "subgroup")], how[x])))
  B <- df[!duplicated(df$subgroup, fromLast = F), c("Group", "subgroup", "grouped")]
  out <- merge(A, B, by = "subgroup")
  return(out[, c("Group", colsToAgg, "grouped")])
}

aggregateIt(df = df, colsToAgg = "Z", how = "sum")
#  Group  Z grouped
#1   110 10      no
#2   112 10      no
#3   113 20      no
#4   113 60     yes
#5   114 50     yes

没有声称这是最有效的解决方案,但它指出了解决方案。希望这会有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-15
    • 1970-01-01
    • 1970-01-01
    • 2021-06-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多