сопоставьте столбцы и извлеките первое число с помощью map_dbl

#r #purrr

Вопрос:

У меня есть следующие данные:

                       08001          08003            08005        08006    08007          08009           08010_AM         08014_AM      0801501
08001    4875.276, 4875.276   8.448, 8.448   16.876, 16.876 14.56, 14.56     NULL 15.066, 15.066     24.953, 24.953     7.229, 7.229 7.074, 7.074
08003                15.632         3663.7           15.794      129.029   13.371        145.019               NULL           12.382      180.696
08005                10.611          4.911         2474.452       29.844     NULL          43.95               NULL          347.076         NULL
08006                 7.291         69.755           29.844     10841.55 4947.665        200.296               NULL           25.047       19.926
08007                  NULL          33.67           17.269     4954.248 3875.372        111.159               NULL             NULL         9.84
08009                32.811        106.313           58.019      145.959  158.566       2791.247               NULL            6.568        12.59
08010_AM     40.875, 40.875           NULL     4.341, 4.341         NULL     NULL           NULL 3039.333, 3039.333     4.341, 4.341         NULL
08014_AM       4.732, 4.732 10.249, 10.249 311.877, 311.877 6.568, 6.568     NULL 10.831, 10.831       4.341, 4.341 420.177, 420.177 6.172, 6.172
0801501              12.344        220.281             NULL       32.741     NULL         18.797               NULL            6.172     1069.609
0801502               23.21        293.464           13.865        34.51    5.624         29.219               NULL             NULL     4779.908
 

Где некоторые столбцы содержат два числовых значения. Я могу использовать следующее для сопоставления по столбцам и взять среднее значение:

   mutate(across(everything(), ~ map_dbl(.x, mean, na.rm = TRUE)))
 

Однако это занимает слишком много времени для всего набора данных. Я замечаю, что все повторяющиеся цифры столбцов одинаковы, поэтому мой вопрос в том, как я могу изменить map функцию, чтобы вместо вычисления среднего значения по двум цифрам взять первое числовое значение?

т. е. в столбце 1 08001 есть значения 40.875, 40.875 , поэтому вместо того, чтобы брать среднее значение этих двух чисел, как я могу просто извлечь первое число? Данные:

 df <- structure(list(`08001` = list(c(4875.276, 4875.276), 15.632, 
    10.611, 7.291, NULL, 32.811, c(40.875, 40.875), c(4.732, 
    4.732), 12.344, 23.21), `08003` = list(c(8.448, 8.448), 3663.7, 
    4.911, 69.755, 33.67, 106.313, NULL, c(10.249, 10.249), 220.281, 
    293.464), `08005` = list(c(16.876, 16.876), 15.794, 2474.452, 
    29.844, 17.269, 58.019, c(4.341, 4.341), c(311.877, 311.877
    ), NULL, 13.865), `08006` = list(c(14.56, 14.56), 129.029, 
    29.844, 10841.553, 4954.248, 145.959, NULL, c(6.568, 6.568
    ), 32.741, 34.51), `08007` = list(NULL, 13.371, NULL, 4947.665, 
    3875.372, 158.566, NULL, NULL, NULL, 5.624), `08009` = list(
    c(15.066, 15.066), 145.019, 43.95, 200.296, 111.159, 2791.247, 
    NULL, c(10.831, 10.831), 18.797, 29.219), `08010_AM` = list(
    c(24.953, 24.953), NULL, NULL, NULL, NULL, NULL, c(3039.333, 
    3039.333), c(4.341, 4.341), NULL, NULL), `08014_AM` = list(
    c(7.229, 7.229), 12.382, 347.076, 25.047, NULL, 6.568, c(4.341, 
    4.341), c(420.177, 420.177), 6.172, NULL), `0801501` = list(
    c(7.074, 7.074), 180.696, NULL, 19.926, 9.84, 12.59, NULL, 
    c(6.172, 6.172), 1069.609, 4779.908), `0801502` = list(c(26.747, 
26.747), 305.18, 17.419, 27.801, 5.624, 20.512, NULL, NULL, 4948.395, 
    3647.42)), row.names = c("08001", "08003", "08005", "08006", 
"08007", "08009", "08010_AM", "08014_AM", "0801501", "0801502"
), class = "data.frame")
 

Ответ №1:

Обновлено Я внес некоторые изменения в свое первое решение, чтобы получить желаемый результат, возникла проблема с выводом, которую я не заметил.

 library(dplyr)
library(purrr)

df %>% 
  map_dfc(~ .x %>% map_dbl(~ if(is.null(.x)) NA else .x[1]))

# A tibble: 10 x 10
   `08001` `08003` `08005`  `08006` `08007` `08009` `08010_AM` `08014_AM` `0801501` `0801502`
     <dbl>   <dbl>   <dbl>    <dbl>   <dbl>   <dbl>      <dbl>      <dbl>     <dbl>     <dbl>
 1 4875.      8.45   16.9     14.6    NA       15.1      25.0        7.23      7.07     26.7 
 2   15.6  3664.     15.8    129.     13.4    145.       NA         12.4     181.      305.  
 3   10.6     4.91 2474.      29.8    NA       44.0      NA        347.       NA        17.4 
 4    7.29   69.8    29.8  10842.   4948.     200.       NA         25.0      19.9      27.8 
 5   NA      33.7    17.3   4954.   3875.     111.       NA         NA         9.84      5.62
 6   32.8   106.     58.0    146.    159.    2791.       NA          6.57     12.6      20.5 
 7   40.9    NA       4.34    NA      NA       NA      3039.         4.34     NA        NA   
 8    4.73   10.2   312.       6.57   NA       10.8       4.34     420.        6.17     NA   
 9   12.3   220.     NA       32.7    NA       18.8      NA          6.17   1070.     4948.  
10   23.2   293.     13.9     34.5     5.62    29.2      NA         NA      4780.     3647. 
 

Или же мы можем использовать map_depth :

 df %>%
  map_depth(2, ~ if(is.null(.x)) NA_real_ else .x[1]) %>%
  map_dfc(~ .x %>% unlist)

# A tibble: 10 x 10
   `08001` `08003` `08005`  `08006` `08007` `08009` `08010_AM` `08014_AM` `0801501` `0801502`
     <dbl>   <dbl>   <dbl>    <dbl>   <dbl>   <dbl>      <dbl>      <dbl>     <dbl>     <dbl>
 1 4875.      8.45   16.9     14.6    NA       15.1      25.0        7.23      7.07     26.7 
 2   15.6  3664.     15.8    129.     13.4    145.       NA         12.4     181.      305.  
 3   10.6     4.91 2474.      29.8    NA       44.0      NA        347.       NA        17.4 
 4    7.29   69.8    29.8  10842.   4948.     200.       NA         25.0      19.9      27.8 
 5   NA      33.7    17.3   4954.   3875.     111.       NA         NA         9.84      5.62
 6   32.8   106.     58.0    146.    159.    2791.       NA          6.57     12.6      20.5 
 7   40.9    NA       4.34    NA      NA       NA      3039.         4.34     NA        NA   
 8    4.73   10.2   312.       6.57   NA       10.8       4.34     420.        6.17     NA   
 9   12.3   220.     NA       32.7    NA       18.8      NA          6.17   1070.     4948.  
10   23.2   293.     13.9     34.5     5.62    29.2      NA         NA      4780.     3647. 
 

Ответ №2:

Вы можете использовать imap_dfc

 library(tibble)
library(purrr)

imap_dfc(df, function(x, y) 
  tibble(!!y := map_dbl(x, ~if(length(.x)) .x[1] else NA)))

#    `08001` `08003` `08005` `08006` `08007` `08009` `08010_AM` `08014_AM`
#     <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>      <dbl>      <dbl>
# 1 4875.      8.45   16.9   1.46e1   NA       15.1      25.0        7.23
# 2   15.6  3664.     15.8   1.29e2   13.4    145.       NA         12.4 
# 3   10.6     4.91 2474.    2.98e1   NA       44.0      NA        347.  
# 4    7.29   69.8    29.8   1.08e4 4948.     200.       NA         25.0 
# 5   NA      33.7    17.3   4.95e3 3875.     111.       NA         NA   
# 6   32.8   106.     58.0   1.46e2  159.    2791.       NA          6.57
# 7   40.9    NA       4.34 NA        NA       NA      3039.         4.34
# 8    4.73   10.2   312.    6.57e0   NA       10.8       4.34     420.  
# 9   12.3   220.     NA     3.27e1   NA       18.8      NA          6.17
#10   23.2   293.     13.9   3.45e1    5.62    29.2      NA         NA   
# … with 2 more variables: 0801501 <dbl>, 0801502 <dbl>
 

Вывод выше-это тиббл, а тибблы не поддерживают имена строк. Если вы хотите сохранить имена строк, воспользуйтесь этим базовым подходом R —

 df[] <- lapply(df, function(x) 
  sapply(x, function(y) if(length(y)) y[1] else NA))
df

#            08001    08003    08005     08006    08007    08009
#08001    4875.276    8.448   16.876    14.560       NA   15.066
#08003      15.632 3663.700   15.794   129.029   13.371  145.019
#08005      10.611    4.911 2474.452    29.844       NA   43.950
#08006       7.291   69.755   29.844 10841.553 4947.665  200.296
#08007          NA   33.670   17.269  4954.248 3875.372  111.159
#08009      32.811  106.313   58.019   145.959  158.566 2791.247
#08010_AM   40.875       NA    4.341        NA       NA       NA
#08014_AM    4.732   10.249  311.877     6.568       NA   10.831
#0801501    12.344  220.281       NA    32.741       NA   18.797
#0801502    23.210  293.464   13.865    34.510    5.624   29.219

#         08010_AM 08014_AM  0801501  0801502
#08001      24.953    7.229    7.074   26.747
#08003          NA   12.382  180.696  305.180
#08005          NA  347.076       NA   17.419
#08006          NA   25.047   19.926   27.801
#08007          NA       NA    9.840    5.624
#08009          NA    6.568   12.590   20.512
#08010_AM 3039.333    4.341       NA       NA
#08014_AM    4.341  420.177    6.172       NA
#0801501        NA    6.172 1069.609 4948.395
#0801502        NA       NA 4779.908 3647.420
 

Комментарии:

1. Спасибо! есть ли в любом случае возможность сохранить имена строк?

2. Я только что добавил базовый подход R, который сохранит имена строк.

3. Спасибо! базовый код R работает, у меня есть список аналогичных фреймов данных, которые я хочу применить lapply , у вас есть какие-либо идеи о том, как я могу применить базовый код R к каждому элементу списка? При необходимости я могу предоставить еще некоторые данные. Моя первоначальная идея состояла в том, чтобы сделать что-то вроде: list %>% map(., ~ imap_dfc(., function(x, y) tibble(!!y := map_dbl(x, ~if(length(.x)) .x[1] else NA))) )

4. Возможно, вам понадобится еще lapply один : df[] <- lapply(list, function(df) lapply(df, function(x) sapply(x, function(y) if(length(y)) y[1] else NA)))

5. Я думаю, что для меня работает следующее mutate(across(where(is.list), ~ map(., first))) , которое просто извлекает первый элемент списка.

Ответ №3:

Мы можем использовать map с summarise

 library(dplyr)
library(purrr)
df %>% 
    summarise(across(everything(),
      ~ map_dbl(., function(x) if(is.null(x)) NA_real_ else x[1])))
      08001   08003    08005    08006    08007    08009 08010_AM 08014_AM  0801501  0801502
1  4875.276   8.448   16.876    14.56       NA   15.066   24.953    7.229    7.074   26.747
2    15.632  3663.7   15.794  129.029   13.371  145.019       NA   12.382  180.696   305.18
3    10.611   4.911 2474.452   29.844       NA    43.95       NA  347.076       NA   17.419
4     7.291  69.755   29.844 10841.55 4947.665  200.296       NA   25.047   19.926   27.801
5        NA   33.67   17.269 4954.248 3875.372  111.159       NA       NA     9.84    5.624
6    32.811 106.313   58.019  145.959  158.566 2791.247       NA    6.568    12.59   20.512
7    40.875      NA    4.341       NA       NA       NA 3039.333    4.341       NA       NA
8     4.732  10.249  311.877    6.568       NA   10.831    4.341  420.177    6.172       NA
9    12.344 220.281       NA   32.741       NA   18.797       NA    6.172 1069.609 4948.395
10    23.21 293.464   13.865    34.51    5.624   29.219       NA       NA 4779.908  3647.42