如何从带有列表列的小标题中提取单个元素,列表列是尺寸不等的列表列表?

时间:2019-01-24 04:16:37

标签: r dplyr tidyverse tidyr purrr

我有一个列表列的小标题。该列表列中的每个元素都是一组命名的键​​-值对,结构为两个列表的列表。密钥称为“ CUSTOM_FIELD_ID”,其值存储在“ FIELD_VALUE”下。

每行的对数和对的顺序在小标题中有所不同。我想用特定的键('CONTACT_FIELD_7')和值('XYZ')对来搜索小标题中的行。

我的想法是完全以某种方式嵌套列表列,并在该文本框中添加其他行,以便每个键值对都有自己的行。然后将两列字符添加到小标题中,一列用于键,一列用于值。但是,我仍然留下一个带有两个列表的列表列(现在每个列表中只有一个元素)。

我还试图找到一种方法来搜索列表列表中的字符串。

这是输入:

library(tidyverse)

df_in <- tibble(CONTACT_ID = c(255381470, 255395936, 255400708, 255952013),
            CUSTOMFIELDS = list(list(list('CUSTOM_FIELD_ID' = 'CONTACT_FIELD_7', 
'FIELD_VALUE' = 'XYZ'), 
                                     list('CUSTOM_FIELD_ID' = 'CONTACT_FIELD_1', 
'FIELD_VALUE' = '123')),
                                list(list('CUSTOM_FIELD_ID' = 'CONTACT_FIELD_2', 
'FIELD_VALUE' = 'abc')),
                                list(list('CUSTOM_FIELD_ID' = 'CONTACT_FIELD_2', 
'FIELD_VALUE' = 'def'), 
                                     list('CUSTOM_FIELD_ID' = 'CONTACT_FIELD_3', 
'FIELD_VALUE' = '1234'),
                                     list('CUSTOM_FIELD_ID' = 'CONTACT_FIELD_7', 
'FIELD_VALUE' = 'XYZ')),
                                list(list('CUSTOM_FIELD_ID' = 'CONTACT_FIELD_1', 
'FIELD_VALUE' = '456'), 
                                     list('CUSTOM_FIELD_ID' = 'CONTACT_FIELD_7', 
'FIELD_VALUE' = 'ZYX'),
                                     list('CUSTOM_FIELD_ID' = 'CONTACT_FIELD_5', 
'FIELD_VALUE' = 'def'))))


# A tibble: 4 x 2
  CONTACT_ID CUSTOMFIELDS
       <dbl> <list>      
1  255381470 <list [2]>  
2  255395936 <list [1]>  
3  255400708 <list [3]>  
4  255952013 <list [3]>  

我目前认为中间输出是

df_out_long <- tibble(CONTACT_ID = c(rep(255381470, 2), 255395936, rep(255400708, 3), rep(255952013, 3)),
                  CUSTOM_FIELD_ID = c('CONTACT_FIELD_7', 'CONTACT_FIELD_1', 'CONTACT_FIELD_2', 
                                      'CONTACT_FIELD_2', 'CONTACT_FIELD_3', 'CONTACT_FIELD_7',
                                      'CONTACT_FIELD_1', 'CONTACT_FIELD_7', 'CONTACT_FIELD_5'),
                  FIELD_VALUE = c('XYZ', '123', 'abc', 'def', '1234', 'XYZ', '456', 'ZYX', 'def'))


# A tibble: 9 x 3
  CONTACT_ID CUSTOM_FIELD_ID FIELD_VALUE
       <dbl> <chr>           <chr>      
1  255381470 CONTACT_FIELD_7 XYZ        
2  255381470 CONTACT_FIELD_1 123        
3  255395936 CONTACT_FIELD_2 abc        
4  255400708 CONTACT_FIELD_2 def        
5  255400708 CONTACT_FIELD_3 1234       
6  255400708 CONTACT_FIELD_7 XYZ        
7  255952013 CONTACT_FIELD_1 456        
8  255952013 CONTACT_FIELD_7 ZYX        
9  255952013 CONTACT_FIELD_5 def    

然后可以轻松过滤出最终所需的结果

df_out_long %>% 
  filter(CUSTOM_FIELD_ID == 'CONTACT_FIELD_7', FIELD_VALUE == 'XYZ')

CONTACT_ID CUSTOM_FIELD_ID FIELD_VALUE
       <dbl> <chr>           <chr>      
1  255381470 CONTACT_FIELD_7 XYZ        
2  255400708 CONTACT_FIELD_7 XYZ   

我被困在哪里

上面的“ df_out_long”可能根本没有必要,因为可能有更有效的方法来执行此操作。但是,沿着这条路径,我可以取消嵌套列表的最深层次,从而在小标题中创建其他行以容纳单独行上的每个键值对。我似乎无法摆脱结果为长度2的列表的列表列,并以某种方式将其展平为两个字符列,即“ CUSTOM_FIELD_ID”和“ FIELD_VALUE”。

df_in %>%
  mutate_if(is.list, simplify_all) %>%
  unnest()

# A tibble: 9 x 2
  CONTACT_ID CUSTOMFIELDS
       <dbl> <list>      
1  255381470 <list [2]>  
2  255381470 <list [2]>  
3  255395936 <list [2]>  
4  255400708 <list [2]>  
5  255400708 <list [2]>  
6  255400708 <list [2]>  
7  255952013 <list [2]>  
8  255952013 <list [2]>  
9  255952013 <list [2]> 

1 个答案:

答案 0 :(得分:1)

一种方法是使用purrr::keep将列表本身过滤为仅关注的元素。然后,一个unnest将过滤掉剩下的行,然后将其变成小标题,以便将其整齐地嵌套。

library(tidyverse)

df_discarded <- df_in %>% mutate(CUSTOMFIELDS = map(
    CUSTOMFIELDS, keep, 
    ~.x$CUSTOM_FIELD_ID == 'CONTACT_FIELD_7' && .x$FIELD_VALUE == 'XYZ'
))

df_discarded
#> # A tibble: 4 x 2
#>   CONTACT_ID CUSTOMFIELDS
#>        <dbl> <list>      
#> 1  255381470 <list [1]>  
#> 2  255395936 <list [0]>  
#> 3  255400708 <list [1]>  
#> 4  255952013 <list [0]>

df_filtered <- df_discarded %>% unnest()
df_filtered
#> # A tibble: 2 x 2
#>   CONTACT_ID CUSTOMFIELDS
#>        <dbl> <list>      
#> 1  255381470 <list [2]>  
#> 2  255400708 <list [2]>

df_out <- df_filtered %>% 
    mutate(CUSTOMFIELDS = map(CUSTOMFIELDS, as_tibble)) %>% 
    unnest()

df_out
#> # A tibble: 2 x 3
#>   CONTACT_ID CUSTOM_FIELD_ID FIELD_VALUE
#>        <dbl> <chr>           <chr>      
#> 1  255381470 CONTACT_FIELD_7 XYZ        
#> 2  255400708 CONTACT_FIELD_7 XYZ

或者,如果您想掌握所有内容并最后使用filter,则bind_rows可以将命名列表变成小标题,然后将其取消嵌套:

df_in %>% 
    mutate(CUSTOMFIELDS = map(CUSTOMFIELDS, bind_rows)) %>% 
    unnest()
#> # A tibble: 9 x 3
#>   CONTACT_ID CUSTOM_FIELD_ID FIELD_VALUE
#>        <dbl> <chr>           <chr>      
#> 1  255381470 CONTACT_FIELD_7 XYZ        
#> 2  255381470 CONTACT_FIELD_1 123        
#> 3  255395936 CONTACT_FIELD_2 abc        
#> 4  255400708 CONTACT_FIELD_2 def        
#> 5  255400708 CONTACT_FIELD_3 1234       
#> 6  255400708 CONTACT_FIELD_7 XYZ        
#> 7  255952013 CONTACT_FIELD_1 456        
#> 8  255952013 CONTACT_FIELD_7 ZYX        
#> 9  255952013 CONTACT_FIELD_5 def