Как извлечь отдельный элемент из таблицы со столбцом списка, который является списком списков с неравными размерами? - PullRequest
0 голосов
/ 24 января 2019

У меня есть столбик со списком. Каждый элемент в этом столбце списка представляет собой набор именованных пар ключ-значение, структурированный как список из двух списков. Ключ называется «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 голосов
/ 24 января 2019

Один из подходов - использовать 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
...