Вычтите минимум для каждого столбца по группе - Добавьте вычтенные значения в другой столбец в df - PullRequest
0 голосов
/ 11 июня 2018

У меня есть нижеприведенный кадр данных:

date    group    col1    col2    col3     col4     col5      
1234        1      -2       3       4       -5      100       
1235        1       4       5      -2       -7      200       
1234        1      -5       2       9        1      400       
1235        1       8       2      -4        7      900       
1235        2     -72      83     -54       98      800      
1233        2      32     -21      -1        4      900      
1342        2     -54       0     -10      -11      100      
1234        2      98      -8      -9      -10      100      

Вот что я хочу сделать:

Для столбцов df [, 3] от второго до последнего столбца, я хочу сделатьследующее:

1) Для каждого столбца возьмите минимальное значение для положительных чисел и минимальное число для отрицательных чисел по группам.

2) Затем замените текущие значения, используя эту логику:

a) Если значение положительное, вычтите минимальное значение, найденное для положительных чисел, по группе.

b) Если значение отрицательное, вычтите минимальное значение, найденное для отрицательных чисел, по группам.

c) Если значение равно 0, не вносить изменений

3) Затем взять итоговые значения, которые были вычтены для каждого значения в этой строке, и добавить его к последнему значению столбца.

Minimum for col1 neg, group 1 = -5
Minimum for col1 pos, group 1 = 4
Minimum for col1 neg, group 2 = -72
Minimum for col1 pos, group 2 = 32
Minimum for col2 neg, group 1 = NA
Minimum for col2 pos, group 1 = 2
etc.  

Я хочу, чтобы мой конечный результат выглядел так:

date    group         col1      col2      col3          col4            col5      
1234        1      -2-(-5)       3-2       4-4       -5-(-7)            100+(-5)+2+4+(-7)       
1235        1         4-4        5-2   -2-(-4)       -7-(-7)            200+4+2+(-4)+(-7)      
1234        1      -5-(-5)       2-2       9-4           1-1               400+(-5)+2+4+1       
1235        1         8-4        2-2   -4-(-4)           7-1               900+4+2+(-4)+1       
1235        2    -72-(-72)     83-83 -54-(-54)          98-4         800+(-72)+83+(-54)+4      
1233        2       32-32  -21-(-21)  -1-(-54)           4-4         900+32+(-21)+(-54)+4      
1342        2    -54-(-72)       0-0 -10-(-54)     -11-(-11)      100+(-72)+0+(-54)+(-11)      
1234        2       98-32   -8-(-21)  -9-(-54)     -10-(-11)     100+32+(-21)+(-54)+(-11) 

Ожидаемый результат:

date    group         col1      col2      col3          col4            col5      
1234        1            3         1         0             2              94       
1235        1            0         3         2             0             195      
1234        1            0         0         5             0             402       
1235        1            4         0         0             6             903       
1235        2            0         0         0            94             761      
1233        2            0         0        53             0             861      
1342        2           18         0        44             0             -37      
1234        2           66        13        45             1              46

1 Ответ

0 голосов
/ 11 июня 2018

После группировки по 'group', mutate столбцы 'col1' до 'col4' со значением min положительных и отрицательных чисел, затем добавьте сумму чисел с помощью 'col5' и обновите 'col5.Позже, обновите 'col1' до 'col4', вычитая из соответствующих столбцов исходного набора данных ('df1')

library(dplyr)
df2 <- df1 %>%
  group_by(group) %>%
  mutate_at(3:6, 
   funs(case_when(. < 0  ~ if(any(. < 0)) as.numeric(min(.[. <0])) else NA_real_,
              . > 0 ~ if(any(. > 0)) as.numeric(min(.[. > 0])) else NA_real_, 
                           TRUE ~ as.numeric(.)))) %>%
   ungroup %>%                        
   mutate(col5 = col5 + rowSums(.[3:6])) 

nm1 <- paste0("col", 1:4)
#nm1 <- 3:6
df2[nm1] <- df1[nm1] - df2[nm1]
df2
# A tibble: 8 x 7
#   date group  col1  col2  col3  col4  col5
#  <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl>
#1  1234     1     3     1     0     2    94
#2  1235     1     0     3     2     0   195
#3  1234     1     0     0     5     0   402
#4  1235     1     4     0     0     6   903
#5  1235     2     0     0     0    94   761
#6  1233     2     0     0    53     0   861
#7  1342     2    18     0    44     0   -37
#8  1234     2    66    13    45     1    46

или используя модификацию с parse_exprs

library(rlang)
expr <- paste(glue::glue('{nm1} - {nm1}_new'), collapse=";")
df1 %>% 
   group_by(group) %>%
   mutate_at(3:6, funs(new = ave(., sign(.), FUN = min))) %>%
   ungroup %>%
   mutate(col5 = col5 + select(., col1_new:col4_new)  %>% 
                    reduce(`+`)) %>% 
   transmute(date, group, !!! parse_exprs(expr), col5) %>%
   rename_at(3:6, ~ nm1)
# A tibble: 8 x 7
#   date group  col1  col2  col3  col4  col5
#  <int> <int> <int> <int> <int> <int> <int>
#1  1234     1     3     1     0     2    94
#2  1235     1     0     3     2     0   195
#3  1234     1     0     0     5     0   402
#4  1235     1     4     0     0     6   903
#5  1235     2     0     0     0    94   761
#6  1233     2     0     0    53     0   861
#7  1342     2    18     0    44     0   -37
#8  1234     2    66    13    45     1    46

Или преобразовать в «длинный» формат для выполнения расчетов, а затем изменить его на «широкий»

library(tidyverse)
df1 %>% 
  rownames_to_column('rn') %>%
  gather(key, val, col1:col4) %>%
  group_by(group, key, sn= sign(val)) %>% 
  mutate(mnVal = min(val)) %>%
  group_by(rn) %>% 
  mutate(col5 = col5 + sum(mnVal), val = val - mnVal) %>% 
  select(-sn, -mnVal) %>%
  spread(key, val) %>%
  ungroup %>% 
  select(names(df1))

data

df1 <- structure(list(date = c(1234L, 1235L, 1234L, 1235L, 1235L, 1233L, 
1342L, 1234L), group = c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L), col1 = c(-2L, 
4L, -5L, 8L, -72L, 32L, -54L, 98L), col2 = c(3L, 5L, 2L, 2L, 
83L, -21L, 0L, -8L), col3 = c(4L, -2L, 9L, -4L, -54L, -1L, -10L, 
-9L), col4 = c(-5L, -7L, 1L, 7L, 98L, 4L, -11L, -10L), col5 = c(100L, 
200L, 400L, 900L, 800L, 900L, 100L, 100L)), .Names = c("date", 
"group", "col1", "col2", "col3", "col4", "col5"), 
  class = "data.frame", row.names = c(NA, 
-8L))
...