【问题标题】:R: Calculate distance between first and current row of grouped dataframeR:计算分组数据帧的第一行和当前行之间的距离
【发布时间】:2019-04-28 00:02:09
【问题描述】:

我需要计算数据框中第一行和当前行之间的欧几里得距离。每行由(组,月)键控,并具有一个值列表。在下面的玩具示例中,键是 c(month, student),值在 c(A, B) 中。我想创建一个距离列 C,它等于 sqrt((A_i-A_1)^2 + (B_i - B_1)^2)。

到目前为止,我设法传播了我的数据并将每个组的第一个值拉入新列。虽然我可以在玩具示例中手动创建公式,但在我的实际数据中,我有很多列而不是只有 2 列。我相信我可以在 mutate_all 中创建平方差,然后进行行求和并取平方根,但到目前为止还没有运气。

df <- data.frame(month=rep(1:3,2),
                 student=rep(c("Amy", "Bob"), each=3),
                 A=c(9, 6, 6, 8, 6, 9),
                 B=c(6, 2, 8, 5, 6, 7))

# Pull in each column's first values for each group
df %>% 
  group_by(student) %>% 
  mutate_all(list(first = first)) %>% 
# TODO: Calculate the distance, i.e. SQRT(sum_i[(x_i - x_1)^2]).

#Output:
  month student     A     B month_first A_first B_first
1     1 Amy         9     6           1       9       6
2     2 Amy         6     2           1       9       6
...

期望的输出:

#Output:
  month student     A     B month_first A_first B_first dist_from_first
1     1 Amy         9     6           1       9       6    0
2     2 Amy         6     2           1       9       6    5
...

【问题讨论】:

  • 你能写出 A 和 B 并且 dist_from_first 是在你想要的输出示例中计算出来的
  • dist_from_first 是 Row 的值与 Row 对应组的第一个 Row 的值之间的距离。 IE。 Sqrt((9-9)^2 + (6-6)^2) = 0, Sqrt((9-6)^2+(6-2)^2) = Sqrt(25)=5。
  • euc_dist % mutate(dist_from_first = euc_dist(A_first,A,B_first,B))

标签: r dplyr distance tidyverse tidyr


【解决方案1】:

这是使用紧凑型dplyr 代码的另一种方式。这可以用于任意数量的列

df %>% 
  select(-month) %>%
  group_by(student) %>% 
  mutate_each(function(x) (first(x) - x)^2) %>%
  ungroup() %>%
  mutate(euc.dist = sqrt(rowSums(select(., -1))))

# A tibble: 6 x 4
  student     A     B euc.dist
  <chr>   <dbl> <dbl>    <dbl>
1 Amy         0     0     0   
2 Amy         9    16     5   
3 Amy         9     4     3.61
4 Bob         0     0     0   
5 Bob         4     1     2.24
6 Bob         1     4     2.24

【讨论】:

    【解决方案2】:

    编辑:添加了使用连接的替代公式。我希望这种方法对于具有许多要比较的列的非常宽的数据框会快得多。

    方法 1: 要获得大量列的欧式距离,一种方法是重新排列数据,使每行显示一个月、一个学生和一个原始列(例如 A 或 B在 OP 中),然后有两列代表当前月份值和第一个值。然后我们可以对差值求平方,并对所有列进行分组以获得欧几里得距离,也就是每个学生月的均方根/RMS。

      library(tidyverse)
      df %>% 
        group_by(student) %>% 
        mutate_all(list(first = first)) %>%
        ungroup() %>%
      # gather into long form; make col show variant, col2 show orig column
      gather(col, val, -c(student, month, month_first)) %>%
      mutate(col2 = col %>% str_remove("_first")) %>% 
      mutate(col = if_else(col %>% str_ends("_first"),
                            "first",
                            "comparison")) %>% 
      spread(col, val) %>% 
      mutate(square_dif = (comparison - first)^2) %>%
      group_by(student, month) %>%
      summarize(RMS = sqrt(sum(square_dif)))
    
    # A tibble: 6 x 3
    # Groups:   student [2]
      student month   RMS
      <fct>   <int> <dbl>
    1 Amy         1  0   
    2 Amy         2  5   
    3 Amy         3  3.61
    4 Bob         1  0   
    5 Bob         2  2.24
    6 Bob         3  2.24
    

    方法 2。 在这里,将数据的长版本加入到每个学生的最早月份的版本中。

    library(tidyverse)
    df_long <- gather(df, col, val, -c(month, student))
    df_long %>% left_join(df_long %>% 
                  group_by(student) %>%
                  top_n(-1, wt = month) %>%
                  rename(first_val = val) %>% 
                  select(-month),
                by = c("student", "col")) %>%
      mutate(square_dif = (val - first_val)^2) %>%
      group_by( student, month) %>%
      summarize(RMS = sqrt(sum(square_dif)))
    
    # A tibble: 6 x 3
    # Groups:   student [2]
      student month   RMS
      <fct>   <int> <dbl>
    1 Amy         1  0   
    2 Amy         2  5   
    3 Amy         3  3.61
    4 Bob         1  0   
    5 Bob         2  2.24
    6 Bob         3  2.24
    

    【讨论】:

    • 方法1是完美的!这让我意识到,在这种情况下,使用长格式实际上比使用宽格式更容易。谢谢!
    • 请注意,如果他的数据很宽,包含 A、B、C、D 等列,这将汇总所有列。这意味着 RMS 将是 sqrt(A_sq_diff + B_sq_diff+C_sq_diff+D_sq_dff)
    • 我相信这是必需的。每行的欧式距离应为sqrt(Adif^2 + Bdif^2 + Cdif^2 + Ddif^2 +....)
    • 但这会进入 n 个维度,对吧?换句话说,D 列表示 4 维距离。我想我认为 OP 是指每两组列的距离。
    【解决方案3】:

    代替mutate_all 调用,直接计算dist_from_first 会更容易。我唯一不清楚的是月份是否应该包含在group_by() 语句中。

    library(tidyverse)
    
    df <- tibble(month=rep(1:3,2),
                     student=rep(c("Amy", "Bob"), each=3),
                     A=c(9, 6, 6, 8, 6, 9),
                     B=c(6, 2, 8, 5, 6, 7))
    
    df%>%
      group_by(student)%>%
      mutate(dist_from_first = sqrt((A - first(A))^2 + (B - first(B))^2))%>%
      ungroup()
    
    # A tibble: 6 x 5
    #  month student     A     B dist_from_first
    #  <int> <chr>   <dbl> <dbl>           <dbl>
    #1     1 Amy         9     6            0   
    #2     2 Amy         6     2            5   
    #3     3 Amy         6     8            3.61
    #4     1 Bob         8     5            0   
    #5     2 Bob         6     6            2.24
    #6     3 Bob         9     7            2.24
    

    【讨论】:

    • 是的,这就是你可以用几列来做到这一点的方法,你可以把它们全部写出来——不过我有几千列要做。
    • 你能扩展你的问题吗?目前尚不清楚您的真实数据集是什么样的。
    • 我有几千列,而不仅仅是 A 和 B 列,即 A、B、C、D、...。
    猜你喜欢
    • 1970-01-01
    • 2017-04-11
    • 1970-01-01
    • 1970-01-01
    • 2018-09-14
    • 1970-01-01
    • 2015-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多