2016-11-03 3 views
1

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

df <- structure(list(Id_no = structure(c(1L, 1L, 1L, 2L, 2L, 3L), .Label = c("n1", 
"n2", "n3"), class = "factor"), Band = structure(c(1L, 2L, 3L, 
1L, 2L, 1L), .Label = c("Band 1", "Band 2", "Band 3"), class = "factor"), 
    median = c(252, 191, 107, 130.5, 61.5, 217), sample_size = c(19L, 
    20L, 1L, 20L, 12L, 1544L)), .Names = c("Id_no", "Band", "median", 
"sample_size"), class = "data.frame", row.names = c(NA, -6L)) 

> df 
    Id_no Band median sample_size 
1 n1 Band 1 252.0   19 
2 n1 Band 2 191.0   20 
3 n1 Band 3 107.0   1 
4 n2 Band 1 130.5   20 
5 n2 Band 2 61.5   12 
6 n3 Band 1 217.0  1544 

Переменная коллапс будет отношение медианы в полосе 1 против означают медиану между диапазонами 2 и 3.

Это будет рассчитываться для Id_no, что на самом деле есть записи как для B1 и по меньшей мере один B2 или B3.

Кроме того, медианные В2 и В3 следует учитывать, только если размер выборки составляет> = 10.

Результирующая таблица Я ищу это:

Id_no b1_vs_rest 
1 n1  1.32 
2 n2  2.12 

Первый шаг, вероятно, чтобы удалить случаи, когда размер выборки не соответствует минимуму:

df <- subset(df, sample_size >=10) 

ответ

1

Один подход с использованием dplyr:

library(dplyr) 
res <- df %>% group_by(Id_no) %>% 
       filter(sample_size >= 10) %>% 
       summarise(b1_vs_rest=median[Band == "Band 1"]/mean(median[Band != "Band 1"])) %>% 
       filter(!is.nan(b1_vs_rest)) 

Примечания:

  1. С dplyr, первый filter держать только те строки, с sample_size гр чем или равным 10.
  2. Тогда group_byId_no и summarise вычислить коэффициент для каждого Id_no выбора для числителе median для Band == "Band 1" и знаменателя медиан для всех других диапазонов.
  3. Если ни "Band 1", ни "Band 2" не существует, то mean вернет NaN, а результат будет также NaN. Итак, мы снова добавим filter, чтобы сохранить только значения не NaN.

В результате с помощью данных, как и ожидалось:

print(res) 
### A tibble: 2 x 2 
## Id_no b1_vs_rest 
## <fctr>  <dbl> 
##1  n1 1.319372 
##2  n2 2.121951 
+0

Извините, вы неправильно поняли вопрос. b1_vs_rest должен быть медианным в B1 по средней медианной величине B2 и B3. Цифры идентичны решению ulfelder, потому что в Id_no = n1 sample_size в B3 равен <10, поэтому удаляется, и поэтому b1_vs_rest = B1/B2; но если sample_size было> 10, цифры были бы разными. – Mihael

+0

Я пробовал модифицировать эту часть: 'b1_vs_rest = f (median [Band ==" Band 1 "])/mean (c (f (median [Band ==" Band 2 "]), f (median [Band == «Группа 3»]))) ', но теперь значения равны 2x, какими они должны быть. Есть идеи? – Mihael

+0

@Mihael: Я вижу, вы правы. См. Мое редактирование. – aichao

1

Вот решение используя dplyr и конструкцию if...else для изменения вывода по вашему состоянию. По вашему вопросу (а не вашим ожидаемым результатам), я предполагаю, что вы хотите, чтобы отношение медианного для группы 1 к среднему значению медианов для групп 2 и 3 в случаях, когда у вас есть все три.

d2 <- df %>% 
    filter(sample_size >= 10) %>% 
    group_by(Id_no) %>% 
    summarise(b1_vs_rest = if(any(Band == "Band 2") & any(Band == "Band 3")) { 
          median[Band == "Band 1"]/(mean(c(median[Band == "Band 2"], median[Band == "Band 3"]))) 
         } else if(any(Band == "Band 2")) { 
          median[Band == "Band 1"]/median[Band == "Band 2"] 
         } else if(any(Band == "Band 3")) { 
          median[Band == "Band 1"]/median[Band == "Band 3"] 
         } else { 
          NA 
         }) %>% 
    filter(!is.na(b1_vs_rest)) 

Результат:

> d2 
# A tibble: 2 × 2 
    Id_no b1_vs_rest 
    <fctr>  <dbl> 
1  n1 1.319372 
2  n2 2.121951 
+0

Спасибо.В этом примере ваш код отлично работает, но когда я пытаюсь использовать реальные данные, он вызывает следующую ошибку: Ошибка в eval (substitute (expr), envir, enc): объект типа «закрытие» не является подмножеством. .. У тебя есть идеи? Очевидно, я заменил соответствующие имена переменных. – Mihael

+0

Трудно сказать, без ваших фактических данных для тестирования. Вероятно, проблема с различиями в именах фреймов или столбцов. Если вы начинаете только с первой трубы и добавляете один шаг поэтапно, в какой момент это происходит? – ulfelder

1

Вот вариант использования data.table. Преобразуйте 'data.frame' в 'data.frame' в 'data.table' (setDT(df)), измените форму с 'long' на 'wide' (dcast), удалите строки NA (na.omit), укажите .SDcols в качестве столбцов «Band», разделите первый столбец (.SD[[1]]) на сумму других столбцов (Reduce(`+`, ...)), чтобы получить ожидаемый результат.

library(data.table)#1.9.7+ 
na.omit(dcast(setDT(df)[sample_size >= 10], Id_no~Band, value.var = "median"))[, 
    .(Id_no, b1_vs_rest = .SD[[1]]/Reduce(`+`, .SD[, -1, with = FALSE])) , .SDcols = -1] 
# Id_no b1_vs_rest 
#1: n1 1.319372 
#2: n2 2.121951 
+0

Я не тестировал ваш код, но новая версия devel больше не требует 'with = FALSE' –

+0

Действительно, использование исходного ответа с' with = FALSE' давало неверные результаты в реальном наборе данных, но удаление этого произведенного правильные результаты, совпадающие с решением aichao – Mihael

+0

@Mihael Я использовал версию devel. Я не знаю, если это имеет значение – akrun