Определяемая пользователем функция для анализа нескольких CSV, создания переменных и подсчета NA по группам в R - PullRequest
2 голосов
/ 01 апреля 2020

У меня примерно 100 наборов данных с разными переменными (и разным количеством переменных), но каждый набор данных имеет идентификатор домохозяйства (hh_ID) в качестве идентификатора. Переменные представляют вопросы опроса. Каждый CSV представляет другой тип опроса. Я хочу написать пользовательскую функцию, которая подсчитывает, сколько раз домашнему хозяйству задавали вопрос и сколько раз они пропустили вопрос (NA). У меня проблема с переименованием переменных и подсчетом в csvs.

Скажем, два кадра данных выглядят так:

hh_ID <- c(1,1,2,2,2)
question1 <- c(NA,1,0,0,0)
question2 <- c(1,1,NA,0,0)
df1 <- data.frame(hh_ID, question1, question2)

hh_ID <- c(1,1,1,2,2)
question3 <- c(NA,NA,0,0,0)
question4 <- c(1,1,1,NA,NA)
df2 <- data.frame(hh_ID, question3, question4)

## > df1
##   hh_ID question1 question2
## 1     1        NA         1
## 2     1         1         1
## 3     2         0        NA
## 4     2         0         0
## 5     2         0         0
## > df2
##   hh_ID question3 question4
## 1     1        NA         1
## 2     1        NA         1
## 3     1         0         1
## 4     2         0        NA
## 5     2         0        NA

Мне нужен окончательный кадр данных, чтобы он выглядел следующим образом:

question1_count <- c(2,3)
question1_NAs   <- c(1,0)
question2_count <- c(2,3)
question2_NAs   <- c(0,1)
question3_count <- c(3,2)
question3_NAs   <- c(2,0)
question4_count <- c(3,2)
question4_NAs <- c(0,2)
finaldf <- data.frame(unique(hh_ID),question1_count, question1_NAs,question2_count,question2_NAs,question3_count,question3_NAs, question4_count,question4_NAs) 

## > finaldf
##   unique.hh_ID. question1_count question1_NAs question2_count question2_NAs question3_count question3_NAs question4_count question4_NAs
## 1             1               2             1               2             0               3             2               3             0
## 2             2               3             0               3             1               2             0               2             2

Это то, что у меня пока есть:

# read in each dta file
filenames <- list.files(path=mydirectory, pattern=".*dta")
for (i in 1:length(filenames)){
assign(filenames[i], read_dta(paste("", filenames[i], sep=''))
)}

variable_NA_count <- function(dataset, col_name){
temp <- dataset %>% group_by(hh_ID) %>% summarise(question_count = n()) 
temp1 <- aggregate(col_name ~ hh_ID, data=dataset, function(x) {sum(is.na(x))}, na.action = NULL)
final <- merge(temp, temp1, by = "hh_ID")
return(final)}

frequency <- function(dataset, col_name){
temp <- variable_NA_count(dataset, col_name)
temp <- temp %>% select(question1_count = question_count,
                        question1_NAs = col_name)}

Проблема в том, что я хочу, чтобы каждое имя переменной заканчивалось на "_count" и "_NAs" без явного написания "question1_count = question_count». У меня есть сотни переменных в csvs, поэтому мне нужна функция, которая читает каждый CSV, читает имя каждого столбца, подсчитывает, сколько раз домохозяйству задавали вопрос и сколько раз они не отвечали. Я пробовал разные способы, например функцию вставки, но продолжаю бить по стене.

Спасибо!

Ответы [ 2 ]

1 голос
/ 01 апреля 2020

Я предлагаю быстрое решение, хотя оно не совсем в том формате, который вы ожидаете.

     list..res <- lapply(list(df1,df2), 
                function(x) setDT(x)[,lapply(.SD,function(x) {  
         list(.N,sum(is.na(x)))}),by=hh_ID][,`:=`(index=1:.N,type=c("count", 
                                                              "no..na")),hh_ID])

Для каждого data.frame я конвертирую его в data.table (library(data.table)), затем для В каждом вопросе я подсчитываю количество вопросов, подсчитываю число NA и подсчитываю количество NA. Наконец, я добавил столбец type и index

 ## + + > list..res
## [[1]]
##    hh_ID question1 question2 index   type
## 1:     1         2         2     1  count
## 2:     1         1         0     2 no..na
## 3:     2         3         3     1  count
## 4:     2         0         1     2 no..na

## [[2]]
##    hh_ID question3 question4 index   type
## 1:     1         3         3     1  count
## 2:     1         2         0     2 no..na
## 3:     2         2         2     1  count
## 4:     2         0         2     2 no..na

Затем мы можем уменьшить этот список путем слияния.

Reduce(function(x,y) merge(x,y,by=c("hh_ID","type","index")), list..res)

##    hh_ID   type index question1 question2 question3 question4
## 1:     1  count     1         2         2         3         3
## 2:     1 no..na     2         1         0         2         0
## 3:     2  count     1         3         3         2         2
## 4:     2 no..na     2         0         1         0         2

Наконец, вместо list(df1,df2) вы можете поместить список фреймов данных.

filenames <- list.files(path=mydirectory, pattern=".*dta")
df..list <- lapply(filenames, read_dta)
1 голос
/ 01 апреля 2020

Вы можете прекрасно использовать функцию dplyr summarize_all:

. Она объединяет все столбцы в df с одной или несколькими заданными функциями, создавая умные имена столбцов (начиная с исходное имя столбца и добавление забавного c name).

library(dplyr)

df1 %>%
  group_by(hh_ID) %>% 
  summarize_all(.funs = list(count = ~n(), NAs = ~sum(is.na(.))))
#> # A tibble: 2 x 5
#>   hh_ID question1_count question2_count question1_NAs question2_NAs
#>   <dbl>           <int>           <int>         <int>         <int>
#> 1     1               2               2             1             0
#> 2     2               3               3             0             1

Создано в 2020-04-01 пакетом Представить (v0.3.0)

Мы можем применить ту же операцию к списку фреймов данных, используя функцию purrr map:

library(dplyr)
library(purrr)

list(df1, df2) %>% 
  map(~{
    .x %>%
      group_by(hh_ID) %>% 
      summarize_all(.funs = list(count = ~n(), NAs = ~sum(is.na(.))))
  }) %>% 
  reduce(full_join)
#> Joining, by = "hh_ID"
#> # A tibble: 2 x 9
#>   hh_ID question1_count question2_count question1_NAs question2_NAs
#>   <dbl>           <int>           <int>         <int>         <int>
#> 1     1               2               2             1             0
#> 2     2               3               3             0             1
#> # … with 4 more variables: question3_count <int>, question4_count <int>,
#> #   question3_NAs <int>, question4_NAs <int>

Создано в 2020-04-01 представьте пакет (v0.3.0)

map возвращает список фреймов данных, но мы хотим присоединиться к ним, используя full_join (или любой другой *_join, который вы считаете соответствующий)

Наконец, мы можем склеить его вместе, читая файлы: list.files(path=mydirectory, pattern=".*dta") возвращает символьный вектор, и мы можем применить map к этому.

Для каждого файла прочитайте его, суммируйте и присоединиться:

library(dplyr)
library(purrr)
library(haven)

list.files(path=mydirectory, pattern=".*dta") %>% 
  map(~{
    read_dta(.x) %>%
      group_by(hh_ID) %>% 
      summarize_all(.funs = list(count = ~n(), NAs = ~sum(is.na(.))))
  }) %>% 
  reduce(full_join)

Создано в 2020-04-01 пакетом Представить (v0.3.0)

(вывод не отображается как У меня нет любой каталог с файлом * .dta)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...