【问题标题】:R Proper use for across() function with na.locf()R 使用 na.locf() 正确使用 cross() 函数
【发布时间】:2021-07-13 00:59:05
【问题描述】:

我正在尝试复制this SO question,但通过使用使用across() 函数的更新语法并摆脱已弃用的summarise_all()funs()

起始数据

我有一个数据库提取每个事件类型的一行,如下所示:

library(tidyverse)
library(zoo)

df_start <- tibble(shipment = c(rep("A",4), rep("B",4)), 
             stop = rep(c(1,1,2,2), 2),
             arrive_pickup = as.POSIXct(c("2021-01-01 07:00:00 UTC",NA, NA, NA,"2021-06-05 12:10:00 UTC", NA, NA, NA)),
             depart_pickup = as.POSIXct(c(NA,"2021-01-01 08:40:00 UTC", NA, NA, NA, "2021-06-05 16:58:00 UTC", NA, NA)),
             arrive_delivery = as.POSIXct(c(NA, NA, "2021-01-05 10:00:00 UTC",NA, NA, NA,"2021-06-08 10:58:00 UTC", NA)),
             depart_delivery = as.POSIXct(c(NA, NA, NA, "2021-01-05 11:30:00 UTC",NA, NA, NA,"2021-06-08 13:50:00 UTC"))
)

> df_start
# A tibble: 8 x 6
  shipment  stop arrive_pickup       depart_pickup       arrive_delivery     depart_delivery    
  <chr>    <dbl> <dttm>              <dttm>              <dttm>              <dttm>             
1 A            1 2021-01-01 07:00:00 NA                  NA                  NA                 
2 A            1 NA                  2021-01-01 08:40:00 NA                  NA                 
3 A            2 NA                  NA                  2021-01-05 10:00:00 NA                 
4 A            2 NA                  NA                  NA                  2021-01-05 11:30:00
5 B            1 2021-06-05 12:10:00 NA                  NA                  NA                 
6 B            1 NA                  2021-06-05 16:58:00 NA                  NA                 
7 B            2 NA                  NA                  2021-06-08 10:58:00 NA                 
8 B            2 NA                  NA                  NA                  2021-06-08 13:50:00

期望结果

...我想通过按装运和停靠点分组,甚至只是按装运分组来折叠行数(我不确定在最终数据框中留下NA 是否会影响答案,但是我正在寻求能够以任何一种方式解决它)。

df_finish1 # 一个期望的结果

# A tibble: 4 x 6
  shipment  stop arrive_pickup       depart_pickup       arrive_delivery     depart_delivery    
  <chr>    <dbl> <dttm>              <dttm>              <dttm>              <dttm>             
1 A            1 2021-01-01 07:00:00 2021-01-01 08:40:00 NA                  NA                 
2 A            2 NA                  NA                  2021-01-05 10:00:00 2021-01-05 11:30:00
3 B            1 2021-06-05 12:10:00 2021-06-05 16:58:00 NA                  NA                 
4 B            2 NA                  NA                  2021-06-08 10:58:00 2021-06-08 13:50:00

df_finish2 # 第二个/替代的期望结果

# A tibble: 2 x 5
  shipment arrive_pickup       depart_pickup       arrive_delivery     depart_delivery    
  <chr>    <dttm>              <dttm>              <dttm>              <dttm>             
1 A        2021-01-01 07:00:00 2021-01-01 08:40:00 2021-01-05 10:00:00 2021-01-05 11:30:00
2 B        2021-06-05 12:10:00 2021-06-05 16:58:00 2021-06-08 10:58:00 2021-06-08 13:50:00

我研究和尝试过的东西

基于this SO question,确实有效:

df_1 <- df_start %>% 
  group_by(shipment, stop) %>%   # Two groupings
  summarise_all(funs(na.locf(., na.rm = FALSE, fromLast = FALSE))) %>% 
  filter(row_number()==n())
  
> df_1
# A tibble: 4 x 6
# Groups:   shipment, stop [4]
  shipment  stop arrive_pickup       depart_pickup       arrive_delivery     depart_delivery    
  <chr>    <dbl> <dttm>              <dttm>              <dttm>              <dttm>             
1 A            1 2021-01-01 07:00:00 2021-01-01 08:40:00 NA                  NA                 
2 A            2 NA                  NA                  2021-01-05 10:00:00 2021-01-05 11:30:00
3 B            1 2021-06-05 12:10:00 2021-06-05 16:58:00 NA                  NA                 
4 B            2 NA                  NA                  2021-06-08 10:58:00 2021-06-08 13:50:00
df_2 <- df_start %>% 
  group_by(shipment) %>%   # Single grouping
  summarise_all(funs(na.locf(., na.rm = FALSE, fromLast = FALSE))) %>% 
  filter(row_number()==n())

> df_2
# A tibble: 2 x 6
# Groups:   shipment [2]
  shipment  stop arrive_pickup       depart_pickup       arrive_delivery     depart_delivery    
  <chr>    <dbl> <dttm>              <dttm>              <dttm>              <dttm>             
1 A            2 2021-01-01 07:00:00 2021-01-01 08:40:00 2021-01-05 10:00:00 2021-01-05 11:30:00
2 B            2 2021-06-05 12:10:00 2021-06-05 16:58:00 2021-06-08 10:58:00 2021-06-08 13:50:00

但是我看到的是 summarise_all() 函数和 funs() 函数已被弃用并且不会继续使用,所以我试图了解如何正确使用 across() 函数,但没有成功:

df_3 <- df_start %>% 
  group_by(shipment) %>% 
  summarise(across(everything()), na.locf(., na.rm = FALSE, fromLast = FALSE))

> df_3 <- df_start %>% 
+   group_by(shipment) %>% 
+   summarise(across(everything()), na.locf(., na.rm = FALSE, fromLast = FALSE))
Error: Problem with `summarise()` input `..2`.
x Input `..2` must be size 4 or 1, not 8.
i An earlier column had size 4.
i Input `..2` is `na.locf(., na.rm = FALSE, fromLast = FALSE)`.
i The error occurred in group 1: shipment = "A".

我已经阅读了描述差异的vignette("colwise"),并建议我只替换上面显示的语法,但显然我没有做对。帮忙?

【问题讨论】:

    标签: r dplyr across coalescing


    【解决方案1】:

    代码中有几个语法问题。

    1 - 参数.cols.fnsacross 内,在您的代码中across 函数在everything() (across(everything())) 之后关闭。

    1. 当您在across 中使用. 时,您需要在它前面加上~ 以指定您正在为传递的函数使用lambda 表达式。 (参见?across 中的.fns 参数)。

    结合您可以使用的这些更改 -

    library(dplyr)
    library(zoo)
    
    df_start %>% 
      group_by(shipment) %>% 
      summarise(across(everything(), ~na.locf(., na.rm = FALSE, fromLast = FALSE)))
    

    但是,across 具有 everything() 作为默认 .cols 参数,您也可以在不需要 ~ 的情况下应用该函数,因此另一种编写方式是 -

    df_start %>% 
      group_by(shipment) %>% 
      summarise(across(.fns = na.locf, na.rm = FALSE, fromLast = FALSE))
    

    【讨论】:

    • 感谢您的回答。我花了 90 分钟尝试一切,除了抓住我放错位置的括号。让它发挥作用对我很有帮助,看到其他应用方式也很棒。非常感谢您的帮助。
    【解决方案2】:

    这里有一个选项,在按“shipment”、“stop”分组后,根据 NA 值对列进行排序,然后 filter 远离所有 NA 的行

    library(dplyr)
    df_start %>%
         group_by(shipment, stop) %>% 
         mutate(across(everything(), ~ .[order(is.na(.))])) %>% 
         filter(!if_all(everything(), is.na)) %>% 
         ungroup
    # A tibble: 4 x 6
      shipment  stop arrive_pickup       depart_pickup       arrive_delivery     depart_delivery    
      <chr>    <dbl> <dttm>              <dttm>              <dttm>              <dttm>             
    1 A            1 2021-01-01 07:00:00 2021-01-01 08:40:00 NA                  NA                 
    2 A            2 NA                  NA                  2021-01-05 10:00:00 2021-01-05 11:30:00
    3 B            1 2021-06-05 12:10:00 2021-06-05 16:58:00 NA                  NA                 
    4 B            2 NA                  NA                  2021-06-08 10:58:00 2021-06-08 13:50:00
    

    对于第二种情况,使用across

    df_start %>% 
       group_by(shipment) %>% 
       dplyr::summarise(across(contains("_"), ~ na.omit(.)))
    # A tibble: 2 x 5
      shipment arrive_pickup       depart_pickup       arrive_delivery     depart_delivery    
      <chr>    <dttm>              <dttm>              <dttm>              <dttm>             
    1 A        2021-01-01 07:00:00 2021-01-01 08:40:00 2021-01-05 10:00:00 2021-01-05 11:30:00
    2 B        2021-06-05 12:10:00 2021-06-05 16:58:00 2021-06-08 10:58:00 2021-06-08 13:50:00
    

    在 OP 中,它使用 na.locf 而不是 na.omit 并且还有一个错字,即 across 在没有任何参数的情况下关闭,即如果我们检查这篇文章中的代码,使用的语法是

    ...across(everything(), ~ .. # correct
    ...across(everything()) ... # incorrect 
    

    因此,我们只需将 ) 与指定用于 lambda 函数的 ~ 一起更改为末尾(否则 function(.) .

    df_start %>% 
      group_by(shipment) %>% 
      summarise(across(everything(), ~ na.locf(., na.rm = FALSE, fromLast = FALSE)))
    

    【讨论】:

      猜你喜欢
      • 2023-02-05
      • 2021-02-18
      • 2021-12-14
      • 1970-01-01
      • 2022-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多