Суммирование значений в R на основе значения столбца с помощью dplyr - PullRequest
0 голосов
/ 09 февраля 2019

У меня есть набор данных, который имеет следующую информацию:

Subject    Value1    Value2    Value3      UniqueNumber
001        1         0         1           3
002        0         1         1           2
003        1         1         1           1

Если значение UniqueNumber> 0, я хотел бы суммировать значения с помощью dplyr для каждого субъекта из строк 1 через UniqueNumber и вычислятьзначение.Таким образом, для субъекта 001 сумма = 2 и среднее значение = .67.

total = 0;
average = 0;
for(i in 1:length(Data$Subject)){
   for(j in 1:ncols(Data)){
   if(Data$UniqueNumber[i] > 0){
    total[i] = sum(Data[i,1:j])
    average[i] = mean(Data[i,1:j])
   }
}

Редактировать: я только рассчитываю сумму по количеству столбцов, перечисленных в столбце «Уникальный номер».Так что это цикл по каждой строке и остановка в столбце, указанном в «UniqueNumber».Пример: строка 2 с субъектом 002 должна суммировать значения в столбцах «Value1» и «Value2», а строка 3 с субъектом 003 должна только суммировать значение в столбце «Value1».

Ответы [ 6 ]

0 голосов
/ 15 февраля 2019

Я думаю, что самый простой способ - установить NA нулей, которые действительно должны быть NA, затем использовать rowSums и rowMeans в соответствующем подмножестве столбцов.

Data[2:4][(col(dat[2:4])>dat[[5]])] <- NA
Data
#   Subject Value1 Value2 Value3 UniqueNumber
# 1       1      1      0      1            3
# 2       2      0      1     NA            2
# 3       3      1     NA     NA            1

library(dplyr)
Data%>%
  mutate(sum  =  rowSums(.[2:4], na.rm = TRUE),
         mean = rowMeans(.[2:4], na.rm = TRUE))

#   Subject Value1 Value2 Value3 UniqueNumber sum      mean
# 1       1      1      0      1            3   2 0.6666667
# 2       2      0      1     NA            2   1 0.5000000
# 3       3      1     NA     NA            1   1 1.0000000

или transform(Data, sum = rowSums(Data[2:4],na.rm = TRUE), mean = rowMeans(Data[2:4],na.rm = TRUE)) чтобы остаться на базе R.

data

Data <- structure(
  list(Subject = 1:3, 
       Value1 = c(1L, 0L, 1L), 
       Value2 = c(0L, 1L, NA), 
       Value3 = c(1L, NA, NA), 
       UniqueNumber = c(3L, 2L, 1L)), 
  .Names = c("Subject","Value1", "Value2", "Value3", "UniqueNumber"),
  row.names = c(NA, 3L), class = "data.frame")
0 голосов
/ 14 февраля 2019

Вот еще один метод, который использует tidyr::nest для сбора столбцов Values в список, чтобы мы могли перебирать таблицу с помощью map2.В каждой строке мы выбираем правильные значения из списка Values и берем сумму или среднее значение соответственно.

library(tidyverse)
tbl <- read_table2(
"Subject    Value1    Value2    Value3      UniqueNumber
001        1         0         1           3
002        0         1         1           2
003        1         1         1           1"
)
tbl %>%
  filter(UniqueNumber > 0) %>%
  nest(starts_with("Value"), .key = "Values") %>%
  mutate(
    sum = map2_dbl(UniqueNumber, Values, ~ sum(.y[1:.x], na.rm = TRUE)),
    mean = map2_dbl(UniqueNumber, Values, ~ mean(as.numeric(.y[1:.x], na.rm = TRUE))),
  )
#> # A tibble: 3 x 5
#>   Subject UniqueNumber Values             sum  mean
#>   <chr>          <dbl> <list>           <dbl> <dbl>
#> 1 001                3 <tibble [1 × 3]>     2 0.667
#> 2 002                2 <tibble [1 × 3]>     1 0.5  
#> 3 003                1 <tibble [1 × 3]>     1 1

Создано в 2019-02-14 с помощью представпакет (v0.2.1)

0 голосов
/ 14 февраля 2019

OP может быть интересен только для решения dplyr, но для целей сравнения и для будущих читателей базовый параметр R, использующий mapply

cols <- grep("^Value", names(df))

cbind(df, t(mapply(function(x, y) {
      if (y > 0) {
        vals = as.numeric(df[x, cols[1:y]])
        c(Sum = sum(vals, na.rm = TRUE), Mean = mean(vals, na.rm = TRUE))
       }
       else 
        c(0, 0)
},1:nrow(df), df$UniqueNumber)))

#  Subject Value1 Value2 Value3 UniqueNumber Sum  Mean
#1       1      1      0      1            3   2 0.667
#2       2      0      1      1            2   1 0.500
#3       3      1      1      1            1   1 1.000

Здесь мы поднастроим каждую строку на основе ее соответствующей UniqueNumberи затем вычислите его sum и mean, если значение UniqueNumber больше 0 или же возвращает только 0.

0 голосов
/ 11 февраля 2019

Проверьте это решение:

df %>%
  gather(key, val, Value1:Value3) %>%
  group_by(Subject) %>%
  mutate(
    Sum = sum(val[c(1:(UniqueNumber[1]))]),
    Mean = mean(val[c(1:(UniqueNumber[1]))]),
  ) %>%
  spread(key, val)

Вывод:

 Subject UniqueNumber   Sum  Mean Value1 Value2 Value3
  <chr>          <int> <dbl> <dbl>  <dbl>  <dbl>  <dbl>
1 001                3     2 0.667      1      0      1
2 002                2     1 0.5        0      1      1
3 003                1     1 1          1      1      1
0 голосов
/ 11 февраля 2019

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

library(tidyr)
library(dplyr)

Data %>% 
  gather(variable, value, -Subject, -UniqueNumber) %>% # long format
  group_by(Subject) %>% # group by Subject in order to get row counts
  filter(row_number() <= UniqueNumber) %>% # filter by row index
  summarise(Mean = mean(value), Total = sum(value)) %>% # do the calculations
  ungroup() 

## A tibble: 3 x 3
#  Subject  Mean Total
#     <int> <dbl> <int>
# 1       1 0.667     2
# 2       2 0.5       1
# 3       3 1         1

Очень похожим способом для достижения этой цели может быть фильтрация поцелые числа в именах столбцов.Этап фильтрации предшествует group_by, поэтому он может потенциально повысить производительность (или нет?), Но он менее надежен, поскольку я предполагаю, что интересующие столбцы называются "Value#"

Data %>% 
  gather(variable, value, -Subject, -UniqueNumber) %>% #long format
  filter(as.numeric(gsub("Value", "", variable, fixed = TRUE)) <= UniqueNumber) %>% #filter
  group_by(Subject) %>% # group by Subject
  summarise(Mean = mean(value), Total = sum(value)) %>% # do the calculations
  ungroup()

## A tibble: 3 x 3
#  Subject  Mean Total
#     <int> <dbl> <int>
# 1       1 0.667     2
# 2       2 0.5       1
# 3       3 1         1

Просто для удовольствия, добавив решение data.table

library(data.table)

data.table(Data) %>% 
  melt(id = c("Subject", "UniqueNumber")) %>%
  .[as.numeric(gsub("Value", "", variable, fixed = TRUE)) <= UniqueNumber,
    .(Mean = round(mean(value), 3), Total = sum(value)),
    by = Subject]

#    Subject  Mean Total
# 1:       1 0.667     2
# 2:       2 0.500     1
# 3:       3 1.000     1
0 голосов
/ 11 февраля 2019

Решение, использующее purrr::map_df (от того же автора, что и dplyr).

library(dplyr)
library(purrr)
l_dat <- split(dat, dat$Subject) # first we need to split in a list

map_df(l_dat, function(x) {
  n_cols <- x$UniqueNumber # finds the number of columns
  x <- as.numeric(x[2:(n_cols+1)]) # subsets x and converts to numeric
  mean(x, na.rm=T) # mean to be returned
})
# output:
# # A tibble: 1 x 3
#     `1`   `2`   `3`
#   <dbl> <dbl> <dbl>
# 1 0.667   0.5     1

Другой вариант (формат вывода ближе к dplyr решению):

map_df(l_dat, function(x) {
  n_cols <- x$UniqueNumber
  id <- x$Subject
  x <- as.numeric(x[2:(n_cols+1)])
  tibble(id=id, mean_values=mean(x, na.rm=T))
})
# # A tibble: 3 x 2
# id mean_values
# <int>       <dbl>
# 1     1       0.667
# 2     2       0.5  
# 3     3       1   

В качестве примера я добавил sum(), затем разделил на length(x)-1:

map_df(l_dat, function(x) {
  n_cols <- x$UniqueNumber
  id <- x$Subject
  x <- as.numeric(x[2:(n_cols+1)])
  tibble(id=id, 
                mean_values=sum(x, na.rm=T)/(length(x)-1)) # change here
})
# # A tibble: 3 x 2
# id mean_values
# <int>       <dbl>
# 1     1          1.
# 2     2          1.
# 3     3        Inf  #beware of this case where you end up dividing by 0

Данные:

tt <- "Subject    Value1    Value2    Value3      UniqueNumber
001        1         0         1           3
002        0         1         1           2
003        1         1         1           1"

dat <- read.table(text=tt, header=T)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...