【问题标题】:How can I sift through a list to find elements that changed?如何筛选列表以查找更改的元素?
【发布时间】:2021-12-28 21:32:08
【问题描述】:

我正在为我的工作解决一个有点复杂的问题。出于解释问题的目的,我将数据简化为非真实世界的数据,但会解释整体概念。

问题:我有一组按帐户划分的数据。对于每个帐户,都会以不同的时间间隔执行盘点,以查看存在哪些项目。通常,从开始日期到每个库存日期,项目应该完全相同。

我们希望查看每个帐户的哪些项目自开始日期起发生了变化。这是数据:

Account Inventory_Date Item
1278 1/1/2020 Apple
1278 1/1/2020 Desk
1278 1/1/2020 Pear
1278 3/10/2021 Apple
1278 3/10/2021 Desk
1278 3/10/2021 Pear
1278 3/10/2021 Grapes
1278 4/15/2021 Apple
1278 4/15/2021 Pear
1278 4/15/2021 Grapes
1239 6/11/2019 Pencils
1239 6/11/2019 Harness
1239 6/11/2019 Toothbrush
1239 7/10/2021 Pencils
1239 7/10/2021 Harness
1239 7/10/2021 Toothbrush
1297 12/20/2018 Apple
1297 1/15/2019 Grapes
1345 3/19/2016 Chicken
1345 3/19/2016 Steak
1345 4/11/2017 Chicken
1345 4/11/2017 Steak
1345 4/11/2017 Ribs

对于 Account 1278,我们从 Apple、Desk 和 Pear 开始。在 3/10,我们可以看到苹果、梨和桌子仍然存在,但添加了葡萄。 然后在 4/15,我们可以看到 Apple、Pear 和 Grapes 存在,所以 Desk 被删除了。

如此预期的结果:添加了葡萄,移除了桌子。就是这样。只是查看从最早的开始日期添加/删除了哪些项目。

对于帐户 1239 - 预期结果:存在相同的项目。没有更改报告。

对于帐户 1297 - 预期结果:Apple 更改为 Grapes。

对于帐户 1345 - 预期结果:添加了肋骨。

我不知道如何解决这个问题。我试过了:

library(tidyverse)
library(readxl)
library(openxlsx)

sampledata <- read_excel("C:/SampleCCData.xlsx") 


sampledata %>% 
  gather(., account, )

但不确定如何进行 - 收集和传播是正确的方法吗?

非常感谢任何帮助!谢谢!

【问题讨论】:

  • (1) 请提供数据,而不是图像,请参阅meta.stackoverflow.com/a/285557(和xkcd.com/2116)。 (2)gather已经被pivot_longer取代了,建议你研究一下……很多方面都比较好。 (3) 请将您的代码限制在问题中,我们这里不需要read_excel(.)(尤其是因为我们没有数据)。
  • 如果可行,将excel数据导入dbms,然后就可以轻松切片了

标签: r tidyverse data-analysis


【解决方案1】:

这是使用 tidyverse 的一种方法:

library(dplyr)
library(tidyr)

mydat <- mydat %>% 
  mutate(Inventory_Date = lubridate::mdy(Inventory_Date))

# Create initial data for later join
init_dat <- mydat %>%
  group_by(Account, Inventory_Date) %>% 
  summarise(Init_Items = list(unique(Item))) %>% 
  group_by(Account) %>% 
  filter(Inventory_Date == min(Inventory_Date)) %>% 
  select(-Inventory_Date)
#> `summarise()` has grouped output by 'Account'. You can override using the
#> `.groups` argument.


mydat %>% 
  # filter all rows expect the first date / initial data
  group_by(Account) %>% 
  filter(Inventory_Date != min(Inventory_Date)) %>% 
  select(-Inventory_Date) %>% 
  # sum Items in list()
  group_by(Account) %>% 
  summarise(Items = list(unique(Item))) %>% 
  # join initial data
  left_join(init_dat, by = "Account") %>% 
  rowwise() %>% 
  # check if items were removed / added
  mutate(added = list(setdiff(Items, Init_Items)),
         removed = list(setdiff(Init_Items, Items))) %>% 
  select(!ends_with("Items")) %>% 
  # unnest/  clean
  unnest(added, keep_empty = TRUE) %>% 
  unnest(removed, keep_empty = TRUE)  

#> # A tibble: 4 x 3
#>   Account added  removed
#>     <int> <chr>  <chr>  
#> 1    1239 <NA>   <NA>   
#> 2    1278 Grapes <NA>   
#> 3    1297 Grapes Apple  
#> 4    1345 Ribs   <NA>

reprex package (v0.3.0) 于 2021 年 12 月 28 日创建

# data

mydat <- data.frame(
  stringsAsFactors = FALSE,
  Account = c(1278L,1278L,1278L,1278L,
              1278L,1278L,1278L,1278L,1278L,1278L,1239L,1239L,
              1239L,1239L,1239L,1239L,1297L,1297L,1345L,1345L,1345L,
              1345L,1345L),
  Inventory_Date = c("1/1/2020","1/1/2020",
                     "1/1/2020","3/10/2021","3/10/2021","3/10/2021","3/10/2021",
                     "4/15/2021","4/15/2021","4/15/2021","6/11/2019",
                     "6/11/2019","6/11/2019","7/10/2021","7/10/2021",
                     "7/10/2021","12/20/2018","1/15/2019","3/19/2016","3/19/2016",
                     "4/11/2017","4/11/2017","4/11/2017"),
  Item = c("Apple","Desk","Pear",
           "Apple","Desk","Pear","Grapes","Apple","Pear","Grapes",
           "Pencils","Harness","Toothbrush","Pencils","Harness",
           "Toothbrush","Apple","Grapes","Chicken","Steak",
           "Chicken","Steak","Ribs")
)

【讨论】:

    【解决方案2】:

    这是一个dplyr 解决方案。关键思想是:添加的是上一条记录中未观察到的,删除的是当前记录中未观察到的。

    library(dplyr)
    
    vsetdiff <- Vectorize(setdiff, c("x", "y"))
    
    sampledata %>% 
      mutate(Inventory_Date = as.Date(Inventory_Date, "%m/%d/%Y")) %>% 
      group_by(Account, Inventory_Date) %>% 
      summarise(Item = list(Item), .groups = "drop_last") %>% 
      mutate(last_item = lag(Item, 1L, Item[1L], Inventory_Date)) %>% 
      summarise(
        added = toString(unlist(vsetdiff(Item, last_item))), 
        removed = toString(unlist(vsetdiff(last_item, Item))), 
      )
    

    结果

    # A tibble: 4 x 3
      Account added    removed
        <int> <chr>    <chr>  
    1    1239 ""       ""     
    2    1278 "Grapes" "Desk" 
    3    1297 "Grapes" "Apple"
    4    1345 "Ribs"   ""   
    

    数据

    sampledata <- structure(list(Account = c(1278L, 1278L, 1278L, 1278L, 1278L, 
    1278L, 1278L, 1278L, 1278L, 1278L, 1239L, 1239L, 1239L, 1239L, 
    1239L, 1239L, 1297L, 1297L, 1345L, 1345L, 1345L, 1345L, 1345L
    ), Inventory_Date = c("1/1/2020", "1/1/2020", "1/1/2020", "3/10/2021", 
    "3/10/2021", "3/10/2021", "3/10/2021", "4/15/2021", "4/15/2021", 
    "4/15/2021", "6/11/2019", "6/11/2019", "6/11/2019", "7/10/2021", 
    "7/10/2021", "7/10/2021", "12/20/2018", "1/15/2019", "3/19/2016", 
    "3/19/2016", "4/11/2017", "4/11/2017", "4/11/2017"), Item = c("Apple", 
    "Desk", "Pear", "Apple", "Desk", "Pear", "Grapes", "Apple", "Pear", 
    "Grapes", "Pencils", "Harness", "Toothbrush", "Pencils", "Harness", 
    "Toothbrush", "Apple", "Grapes", "Chicken", "Steak", "Chicken", 
    "Steak", "Ribs")), row.names = c(NA, -23L), class = c("tbl_df", 
    "tbl", "data.frame"))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-06
      • 2017-12-03
      • 2021-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-01
      • 2020-02-15
      相关资源
      最近更新 更多