Назначьте новый столбец во фрейме данных, содержащий отсортированные группы - PullRequest
0 голосов
/ 26 мая 2018

Представлен фрейм данных со строками, содержащими отсортированные сгруппированные данные.Необходимо ввести новый столбец, значения которого зависят от значений некоторого столбца.

Если первое значение равно нулю, то все значения для группы получают первое ненулевое значение или NA, если нет такого значения.В противном случае, если первое значение ненулевое, назначается фиксированное значение, например, -1.

Пример кадра входных данных:

df <- data.frame(
 name = c("A", "A", "A", "A", "B", "B", "C", "C"), 
 value = c(0, 0, 6, 3, 0, 0 , 7, 0))

Пример кадра выходных данных с calc колонка создана:

df <- data.frame(
 name = c("A", "A", "A", "A", "B", "B", "C", "C"), 
 value = c(0, 0, 6, 3, 0, 0 , 7, 0),
 calc = c(6, 6, 6, 6, NA, NA, -1, -1))

Заранее спасибо.

PS: предпочтительна база R

Ответы [ 4 ]

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

Вот один из способов сделать это с базой R:

df$calc <- unlist(tapply(df$value, df$name, function(x) rep(if(x[1]==0) x[x!=0][1] else -1, length(x))))

... и лучший способ:

df$calc <- ave(df$value, df$name, FUN = function(x) if(x[1]==0) x[x!=0][1] else -1)

Это лучше понять в двух частях:

Сначала напишите функцию, которая соответствует вашим желаемым условиям.

 doit <- function(x) if(x[1]==0) x[x!=0][1] else -1

Во-вторых, используйте его в ave:

 ave(df$value, df$name, FUN=doit)

| edit |

Как следует изменить функцию, если я хочуприсвоить столбцу «calc» значение из другого столбца, например, «value2», соответствующее первому ненулевому «значению»?

Здесь ave вам больше не поможет, вам потребуется split фрейм данных и присоединиться к нему.

df$value2 <- 101:108
do.call(rbind, lapply(split(df, df$name), function(x) {
  x $ calc <- with(x, ifelse(value[1]==0, value[value!=0][1], value2[value2!=0][1]))
  x
}))

Обратите внимание на второй рядв function(x) ... это для , возвращающего всего x вместо только $calc компонента.Логический порядок такой: split -> lapply -> do.call, но он выглядит наоборот, из-за того, как работают скобки.Можно переписать это, используя каналы из magrittr, чтобы сохранить логический порядок (канал LHS %>% RHS направляет LHS в качестве первого аргумента RHS, поэтому необходим трюк с do.call, где мы хотим, чтобы он был вторым аргументом.).

library(magrittr)
split(df, df$name) %>%
  lapply(function(x) {
    x $ calc <- with(x, ifelse(value[1]==0, value[value!=0][1], value2[value2!=0][1]))
    x
  }) %>% {do.call(rbind, .)}
0 голосов
/ 26 мая 2018

Я согласен с ответом @ andrew_reece.Вы можете даже пропустить line max(value) == 0 ~ NA_real_, так как calc будет в любом случае установлен на NA, поэтому «может быть короче, чтобы написать:

df %>% 
  group_by(name) %>% 
  mutate(calc = ifelse(first(value) == 0, value[value != 0][1], -1))
0 голосов
/ 26 мая 2018

Вот вариант с data.table

library(data.table)
setDT(df)[, calc := if(!value[1]) value[value != 0][1] else -1, name]
df
#   name value calc
#1:    A     0    6
#2:    A     0    6
#3:    A     6    6
#4:    A     3    6
#5:    B     0   NA
#6:    B     0   NA
#7:    C     7   -1
#8:    C     0   -1
0 голосов
/ 26 мая 2018

Вы можете использовать group_by и case_when с dplyr:

library(tidyverse)

df %>%
  group_by(name) %>%
  mutate(calc = case_when(
    first(value) != 0 ~ -1., 
    max(value) == 0 ~ NA_real_,
    TRUE ~ value[value != 0][1]))

Выход:

# A tibble: 8 x 3
# Groups:   name [3]
  name  value  calc
  <fct> <dbl> <dbl>
1 A        0.    6.
2 A        0.    6.
3 A        6.    6.
4 A        3.    6.
5 B        0.   NA 
6 B        0.   NA 
7 C        7.   -1.
8 C        0.   -1.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...