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

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

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%>% melt (idvars = 'ID')

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

Ответы [ 2 ]

0 голосов
/ 27 октября 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 для идентификатора, который является все NA, и аналогично для других столбцов теста *.

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)
0 голосов
/ 27 октября 2018

Мы можем сделать группу 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))
...