R: Подсчет вхождений в каждом столбце и замена значения этого столбца количеством (SQL?)

Вот пример исходных данных:

ID        Test1    Test2       Test3      Test4

1          0         0          NA         1.2

1          0         NA         NA         3.0 

1          NA        NA         NA          0 

2          0         0          0           0

2          0         0          NA          NA

Я хочу подсчитать, сколько вхождений, не относящихся к NA (включая 0) для каждого идентификатора, и заменить значение этого столбца этим числом. Производство этого:

ID        Test1    Test2       Test3      Test4

1           2        1          NA          3

2           2        2          1           1

Я не знаю, нужно ли мне использовать пакет sqldf в R. Я попытался преобразовать фрейм данных в таблицу данных и изменить его форму, но это не сработало.

df ‹- x %>% расплава (idvars = 'ID')

Спасибо за помощь.


person aspratle    schedule 26.10.2018    source источник


Ответы (2)


Мы можем сделать группу sum на логическом векторе

library(dplyr)
df1 %>% 
  group_by(ID) %>% 
  summarise_all(funs(na_if(sum(!is.na(.)), 0)))
# A tibble: 2 x 5
#     ID Test1 Test2 Test3 Test4
#  <int> <int> <int> <int> <int>
#1     1     2     1    NA     3
#2     2     2     2     1     1

Или используйте aggregate из base R

aggregate(.~ ID, df1, FUN = function(x) sum(!is.na(x)), na.action = NULL)

Или с rowsum

rowsum(+(!is.na(df1[-1])), df1$ID)

данные

df1 <- structure(list(ID = c(1L, 1L, 1L, 2L, 2L), Test1 = c(0L, 0L, 
NA, 0L, 0L), Test2 = c(0L, NA, NA, 0L, 0L), Test3 = c(NA, NA, 
NA, 0L, NA), Test4 = c(1.2, 3, 0, 0, NA)), class = "data.frame", 
row.names = c(NA, -5L))
person akrun    schedule 26.10.2018

Ниже мы обсудим решения с использованием двух пакетов, упомянутых в вопросе.

1) sqldf Чтобы использовать пакет sqldf, указанный в вопросе, используя ввод, воспроизводимый в примечании в конце:

library(sqldf)
sqldf("select ID, 
              nullif(count(Test1), 0) Test1,
              nullif(count(Test2), 0) Test2,
              nullif(count(Test3), 0) Test3,
              nullif(count(Test4), 0) Test4
       from DF
       group by ID")

давая:

  ID Test1 Test2 Test3 Test4
1  1     2     1    NA     3
2  2     2     2     1     1

nullif(count(test1), 0) можно сократить до count(test1), если можно сообщить 0 для идентификатора, который является полностью Н/П, и аналогичным образом для других столбцов теста*.

1a) Если на самом деле столбцов много, а не только 4, или вам не нравится повторяющаяся часть select, мы можем создать строку, а затем вставить ее следующим образом:

testNames <- names(DF)[-1]
select <- toString(sprintf("nullif(count(%s), 0) %s", testNames, testNames))

library(sqldf)
fn$sqldf("select ID, $select
       from DF
       group by ID")

Добавьте аргумент verbose = TRUE к вызову sqldf, чтобы увидеть, что та же самая строка фактически отправляется на серверную часть.

Если можно указать 0 вместо NA, мы могли бы упростить select <- ... до:

select <- toString(sprintf("count(%s) %s", testNames, testNames))

2) reshape2 Чтобы использовать melt, как в попытке кода в вопросе:

library(magrittr)
library(reshape2)

count <- function(x) if (all(is.na(x))) NA_integer_ else sum(!is.na(x))

DF %>% 
  melt(id.vars = "ID") %>% 
  dcast(ID ~ variable, count)

Если можно сообщить 0 для любого идентификатора, который полностью NA, тогда подсчет можно упростить до:

count <- function(x) sum(!is.na(x))

Примечание

Lines <- "ID        Test1    Test2       Test3      Test4
1          0         0          NA         1.2
1          0         NA         NA         3.0 
1          NA        NA         NA          0 
2          0         0          0           0
2          0         0          NA          NA"
DF <- read.table(text = Lines, header = TRUE)
person G. Grothendieck    schedule 27.10.2018