Масштабировать столбцы фрейма данных в процентах от базовой записи с помощью dplyr - PullRequest
0 голосов
/ 22 мая 2018

Мне часто нужно масштабировать временные ряды относительно их значения в определенное базовое время (обычно в процентах от базового уровня).Вот пример.

> library(dplyr)
> library(magrittr)
> library(tibble)
> library(tidyr)
# [messages from package imports snipped]
> set.seed(42)
> mexico <- tibble(Year=2000:2004, Country='Mexico', A=10:14+rnorm(5), B=20:24+rnorm(5))
> usa <- tibble(Year=2000:2004, Country='USA', A=30:34+rnorm(5), B=40:44+rnorm(5))
> table <- rbind(mexico, usa)
> table
# A tibble: 10 x 4
    Year Country     A     B
   <int> <chr>   <dbl> <dbl>
 1  2000 Mexico   11.4  19.9
 2  2001 Mexico   10.4  22.5
 3  2002 Mexico   12.4  21.9
 4  2003 Mexico   13.6  25.0
 5  2004 Mexico   14.4  23.9
 6  2000 USA      31.3  40.6
 7  2001 USA      33.3  40.7
 8  2002 USA      30.6  39.3
 9  2003 USA      32.7  40.6
10  2004 USA      33.9  45.3

Я хочу масштабировать A и B, чтобы выразить каждое значение в процентах от значения 2001 года для конкретной страны (т. Е. Записи A и Bв строках 2 и 7 должно быть 100).Мой способ сделать это несколько окольным и неудобным: извлечь базовые значения в отдельную таблицу, объединить их обратно в отдельный столбец в основной таблице, а затем вычислить масштабированные значения с раздражающим промежуточным сбором и распространением, чтобы избежать указания имен столбцовкаждого временного ряда (реальные наборы данных могут иметь более двух столбцов значений).Есть ли лучший способ сделать это, в идеале с одним коротким конвейером?

> long_table <- table %>% gather(variable, value, -Year, -Country)
> long_table
# A tibble: 20 x 4
    Year Country variable value
   <int> <chr>   <chr>    <dbl>
 1  2000 Mexico  A         11.4
 2  2001 Mexico  A         10.4
#[remaining tibble printout snipped]
> baseline_table <- long_table %>%
    filter(Year == 2001) %>%
    select(-Year) %>%
    rename(baseline=value)
> baseline_table
# A tibble: 4 x 3
  Country variable baseline
  <chr>   <chr>       <dbl>
1 Mexico  A            10.4
2 USA     A            33.3
3 Mexico  B            22.5
4 USA     B            40.7
> normalized_table <- long_table %>%
  inner_join(baseline_table) %>% 
  mutate(value=100*value/baseline) %>%
  select(-baseline) %>%
  spread(variable, value) %>%
  arrange(Country, Year)
Joining, by = c("Country", "variable")
> normalized_table
# A tibble: 10 x 4
    Year Country     A     B
   <int> <chr>   <dbl> <dbl>
 1  2000 Mexico  109.   88.4
 2  2001 Mexico  100.  100
 3  2002 Mexico  118.   97.3
 4  2003 Mexico  131.  111.
 5  2004 Mexico  138.  106.
 6  2000 USA      94.0  99.8
 7  2001 USA     100   100
 8  2002 USA      92.0  96.6
 9  2003 USA      98.3  99.6
10  2004 USA     102.  111.

Моя вторая попытка была использовать transform, но это не удалось, потому что transform, кажется, не распознает dplyr групп, и это было бы неоптимальным, даже если бы это работало, потому что мне нужно знать, что 2001 год - второй год во временном ряду.

> table %>%
  arrange(Country, Year) %>%
  gather(variable, value, -Year, -Country) %>%
  group_by(Country, variable) %>%
  transform(norm=value*100/value[2])
   Year Country variable    value     norm
1  2000  Mexico        A 11.37096 108.9663
2  2001  Mexico        A 10.43530 100.0000
3  2002  Mexico        A 12.36313 118.4741
4  2003  Mexico        A 13.63286 130.6418
5  2004  Mexico        A 14.40427 138.0340
6  2000     USA        A 31.30487 299.9901
7  2001     USA        A 33.28665 318.9811
8  2002     USA        A 30.61114 293.3422
9  2003     USA        A 32.72121 313.5627
10 2004     USA        A 33.86668 324.5395
11 2000  Mexico        B 19.89388 190.6402
12 2001  Mexico        B 22.51152 215.7247
13 2002  Mexico        B 21.90534 209.9157
14 2003  Mexico        B 25.01842 239.7480
15 2004  Mexico        B 23.93729 229.3876
16 2000     USA        B 40.63595 389.4085
17 2001     USA        B 40.71575 390.1732
18 2002     USA        B 39.34354 377.0235
19 2003     USA        B 40.55953 388.6762
20 2004     USA        B 45.32011 434.2961

Ответы [ 2 ]

0 голосов
/ 23 мая 2018

Вдохновленный ответом Камиллы, я нашел один простой подход, который хорошо масштабируется:

table %>%
  gather(variable, value, -Year, -Country) %>%
  group_by(Country, variable) %>%
  mutate(value=100*value/value[Year == 2001]) %>%
  spread(variable, value)
# A tibble: 10 x 4
# Groups:   Country [2]
    Year Country     A     B
   <int> <chr>   <dbl> <dbl>
 1  2000 Mexico  109.   88.4
 2  2000 USA      94.0  99.8
 3  2001 Mexico  100.  100
 4  2001 USA     100   100
 5  2002 Mexico  118.   97.3
 6  2002 USA      92.0  96.6
 7  2003 Mexico  131.  111.
 8  2003 USA      98.3  99.6
 9  2004 Mexico  138.  106.
10  2004 USA     102.  111.

Сохранение исходных значений вместе с масштабированными требует больше работы.Вот два подхода.Один из них использует дополнительный вызов gather для создания двух столбцов с именами переменных (один указывает на название серии, другой маркирует original или scaled), затем объединяет их в один столбец и переформатирует.

table %>%
  gather(variable, original, -Year, -Country) %>%
  group_by(Country, variable) %>%
  mutate(scaled=100*original/original[Year == 2001]) %>%
  gather(scaled, value, -Year, -Country, -variable) %>% 
  unite(variable_scaled, variable, scaled, sep='_') %>% 
  mutate(variable_scaled=gsub("_original", "", variable_scaled)) %>% 
  spread(variable_scaled, value)
# A tibble: 10 x 6
# Groups:   Country [2]
    Year Country     A A_scaled     B B_scaled
   <int> <chr>   <dbl>    <dbl> <dbl>    <dbl>
 1  2000 Mexico   11.4    109.   19.9     88.4
 2  2000 USA      31.3     94.0  40.6     99.8
 3  2001 Mexico   10.4    100.   22.5    100
 4  2001 USA      33.3    100    40.7    100
 5  2002 Mexico   12.4    118.   21.9     97.3
 6  2002 USA      30.6     92.0  39.3     96.6
 7  2003 Mexico   13.6    131.   25.0    111.
 8  2003 USA      32.7     98.3  40.6     99.6
 9  2004 Mexico   14.4    138.   23.9    106.
10  2004 USA      33.9    102.   45.3    111.

Второй эквивалентный подход создает новую таблицу со столбцами, масштабированными «на месте», а затем объединяет ее с исходной.

table %>% 
  gather(variable, value, -Year, -Country) %>%
  group_by(Country, variable) %>%
  mutate(value=100*value/value[Year == 2001]) %>%
  ungroup() %>%
  mutate(variable=paste(variable, 'scaled', sep='_')) %>% 
  spread(variable, value) %>%
  inner_join(table)
Joining, by = c("Year", "Country")
# A tibble: 10 x 6
    Year Country A_scaled B_scaled     A     B
   <int> <chr>      <dbl>    <dbl> <dbl> <dbl>
 1  2000 Mexico     109.      88.4  11.4  19.9
 2  2000 USA         94.0     99.8  31.3  40.6
 3  2001 Mexico     100.     100    10.4  22.5
 4  2001 USA        100      100    33.3  40.7
 5  2002 Mexico     118.      97.3  12.4  21.9
 6  2002 USA         92.0     96.6  30.6  39.3
 7  2003 Mexico     131.     111.   13.6  25.0
 8  2003 USA         98.3     99.6  32.7  40.6
 9  2004 Mexico     138.     106.   14.4  23.9
10  2004 USA        102.     111.   33.9  45.3

Можно заменить окончательную inner_join наarrange(County, Year) %>% select(-Country, -Year) %>% bind_cols(table), который может работать лучше для некоторых наборов данных, хотя и упорядочивает столбцы неоптимально.

0 голосов
/ 23 мая 2018

Было бы неплохо, чтобы это было более масштабируемым, но вот простое решение.Вы можете сослаться на A[Year == 2001] внутри mutate, так же, как вы могли бы сделать table$A[table$Year == 2001] в базе R. Это позволяет вам масштабироваться относительно базового уровня 2001 года или любого другого года, который вам может понадобиться.

Изменить: Я пропустил group_by, чтобы гарантировать, что значения масштабируются только для других значений в их собственной группе.«Проверка работоспособности» (которую я явно не делал) заключается в том, что значения для Мексики в 2001 году должны иметь масштабированное значение 1, и то же самое для США и любых других стран.

library(tidyverse)
set.seed(42)
mexico <- tibble(Year=2000:2004, Country='Mexico', A=10:14+rnorm(5), B=20:24+rnorm(5))
usa <- tibble(Year=2000:2004, Country='USA', A=30:34+rnorm(5), B=40:44+rnorm(5))
table <- rbind(mexico, usa)

table %>%
  group_by(Country) %>%
  mutate(A_base2001 = A / A[Year == 2001], B_base2001 = B / B[Year == 2001])
#> # A tibble: 10 x 6
#> # Groups:   Country [2]
#>     Year Country     A     B A_base2001 B_base2001
#>    <int> <chr>   <dbl> <dbl>      <dbl>      <dbl>
#>  1  2000 Mexico   11.4  19.9      1.09       0.884
#>  2  2001 Mexico   10.4  22.5      1          1    
#>  3  2002 Mexico   12.4  21.9      1.18       0.973
#>  4  2003 Mexico   13.6  25.0      1.31       1.11 
#>  5  2004 Mexico   14.4  23.9      1.38       1.06 
#>  6  2000 USA      31.3  40.6      0.940      0.998
#>  7  2001 USA      33.3  40.7      1          1    
#>  8  2002 USA      30.6  39.3      0.920      0.966
#>  9  2003 USA      32.7  40.6      0.983      0.996
#> 10  2004 USA      33.9  45.3      1.02       1.11

Создано в 2018 году-05-23 представьте пакет (v0.2.0).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...