R - читать файл Excel и переключать переменные в наблюдения - PullRequest
0 голосов
/ 23 ноября 2018

Я борюсь с некоторым преобразованием данных.У меня есть несколько огромных файлов xlsx со страховыми данными.Данные структурированы немного как «пирамида».Первая строка представляет квартал, в котором проводился опрос.Следующая строка - это разбивка по возрастным категориям.Есть 4 категории: в общих значениях, до 17, 18-64 и 65+.Один лист содержит 4 квартала, поэтому в основном 48 уникальных переменных и столбец с названиями стран.Один файл Excel содержит 3 листа (2016, 2017 и 2018).Снимок экрана (ВХОДНЫЕ ДАННЫЕ) взят из файла Excel, который называется «синие воротнички».У меня также есть два других файла: "работник по болезни" и "самозанятость по болезни".Цель состоит в том, чтобы объединить все три файла и создать файл со структурой, как в РЕЗУЛЬТАТАХ ДАННЫХ.Не могли бы вы мне помочь?

ВХОДНЫЕ ДАННЫЕ: enter image description here

РЕЗУЛЬТАТЫ ДАННЫХ: enter image description here

1 Ответ

0 голосов
/ 23 ноября 2018

Вот решение, в котором используются пакеты readxl и tidyr из Tidyverse .Чтобы сделать сценарий воспроизводимым, я создал версию снимка экрана OP для Excel и сохранил ее в своем репозитории stackoverflowAnswers github.Сценарий загружает файл Excel, читает его и преобразует в формат данных Tidy.

# download Excel file from github repository

sourceFile <- "https://raw.githubusercontent.com/lgreski/stackoverflowanswers/master/data/soQuestion53446800.xlsx"
destinationFile <- "./soQuestion53446800.xlsx"
download.file(sourceFile,destinationFile,mode="wb")


library(readxl)
library(tidyr)

# set constants 
typeOfLeave <- "sick"
group <- "self employed"

# read date and extract the value
theDate <- read_excel(destinationFile,range="A2:A2",col_names=FALSE)[[1]]

# setup column names using underscore so we can separate key column into Sex and Age columns 
theCols <- c("Country","both_all","women_all","men_all","both_up to 17","women_up to 17","men_up to 17")
theData <- read_excel(destinationFile,range="A5:G9",col_names=theCols)

# use tidyr / dplyr to transform the data
theData %>% gather(.,key="key",value="Amount",2:7) %>% separate(.,key,into=c("Sex","Age"),sep="_") -> tidyData

# assign constants

tidyData$typeOfLeave <- typeOfLeave
tidyData$group <- group
tidyData$date <- theDate

tidyData 

... и вывод:

> tidyData
# A tibble: 30 x 7
   Country    Sex   Age   Amount typeOfLeave group         date               
   <chr>      <chr> <chr>  <dbl> <chr>       <chr>         <dttm>             
 1 Total      both  all   151708 sick        self employed 2016-03-31 00:00:00
 2 Afganistan both  all      269 sick        self employed 2016-03-31 00:00:00
 3 Albania    both  all      129 sick        self employed 2016-03-31 00:00:00
 4 Algeria    both  all      308 sick        self employed 2016-03-31 00:00:00
 5 Andora     both  all      815 sick        self employed 2016-03-31 00:00:00
 6 Total      women all    49919 sick        self employed 2016-03-31 00:00:00
 7 Afganistan women all      104 sick        self employed 2016-03-31 00:00:00
 8 Albania    women all       30 sick        self employed 2016-03-31 00:00:00
 9 Algeria    women all       18 sick        self employed 2016-03-31 00:00:00
10 Andora     women all      197 sick        self employed 2016-03-31 00:00:00
# ... with 20 more rows

Ключевые элементы в решении

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

В этом разделе мы объясним некоторые из ключевых элементов дизайна в решении проблемы, поставленной в OP, включая:

  1. Чтение файлов Excel через точные ссылки на ячейки с помощью readxl::read_excel()
  2. Считывание одной ячейки в константу
  3. Настройка имен столбцов для удобства использования с tidyr::separate()
  4. Реструктуризация в узкий формат Tidy Data
  5. Назначение констант

1.Чтение точных ссылок на ячейки

В вопросе OP отмечается, что существует строка заголовка, содержащая дату для всех ячеек в конкретной таблице.Чтобы смоделировать это в примере электронной таблицы, которую я использовал для копирования снимка экрана в OP, я назначил дату 31 марта 2016 года ячейке A2 из Sheet 1 в книге Excel.

readxl::read_excel()позволяет читать точные ссылки на ячейки с аргументом range=.

2.Считывание константы из одной ячейки

Если мы установим аргумент range= в одну ячейку и извлечем ячейку с помощью формы [[ оператора извлечения, то получающийся объект будет одним элементом вектора вместофрейм данных.Это позволяет использовать векторную переработку для присвоения этого значения фрейму аккуратных данных позже в сценарии R.

Поскольку все в R является объектом, мы можем использовать оператор извлечения [[ для результата read_excel(), чтобы присвоить результат theDate.

theDate <- read_excel(theXLSX,range="A2:A2",col_names=FALSE)[[1]]

3.Установка имен столбцов для удобства использования с tidyr::separate()

Одной из характеристик, которая делает исходную электронную таблицу грязной, в отличие от Tidy Data , является тот факт, что каждый столбец данных представляет собой комбинациюSex и Age значения.

Требуемый фрейм выходных данных включает столбцы для Sex и Age, и поэтому нам нужен способ извлечь эту информацию из имен столбцов.Пакет tidyr предоставляет функцию для поддержки этой техники, функцию separate().

Чтобы упростить использование этой функции, мы присваиваем имена столбцов с разделителем подчеркивания, чтобы различать компоненты Sex и Age в именах столбцов.

    theCols <- c("Country","both_all","women_all","men_all","both_up to 17","women_up to 17","men_up to 17")

4.Реструктуризация данных в узкий формат Tidy Data

Ключевым шагом в скрипте является последовательность функций Tidyverse, которая принимает кадр данных, считанный с помощью read_excel(), использует tidyr::gather() в столбцах 2 - 7 для создания одной строкидля каждой уникальной комбинации Страна, Пол и Возраст, а затем разбивает полученный столбец key на столбцы Sex и Age.

theData %>% gather(.,key="key",value="Amount",2:7) %>% separate(.,key,into=c("Sex","Age"),sep="_") -> tidyData

Данные слева от подчеркивания назначаются столбцу Sex, а справа от подчеркивания - Age.Обратите внимание, что OP не определяет, как итоги должны обрабатываться в выходных данных.Поскольку total не имеет смысла в качестве значения для Sex, я использовал вместо него Both.Точно так же для Age я назначил total как All.

5.Назначение констант

ОП не объясняет, откуда взяты константы sick и group, поэтому я назначил их в качестве констант в начале программы.Если они включены в иерархическую часть электронной таблицы, их можно легко прочитать, используя технику, которую я использовал для извлечения даты из электронной таблицы.

После того, как данные представлены в аккуратном формате, мы добавляем оставшиеся константы через оператор присваивания, используя векторную переработку в R.

tidyData$typeOfLeave <- typeOfLeave
tidyData$group <- group
tidyData$date <- theDate

Дополнительные замечания

Если значения total не требуются во фрейме выходных данных, их можно легко удалить с помощью оператора извлечения аккуратных данных или отбрасывания столбцов из фрейма грязных данных перед использованием gather().

Обратите внимание, что я решил оставить итоги во фрейме выходных данных, поскольку почти все данные на снимке экрана представляют итоги той или иной формы (т. Е. Только 2 из 30 ячеек данных на экране OP).захват не был итоговым), и удаление этих данных затруднило бы подтверждение того, что скрипт работал правильно.

Решение можно расширить, чтобы охватить возрастные категории, на которые есть ссылки в ОП, но не показано в электронной таблице, добавив соответствующие имена столбцов в вектор theCols и изменив аргумент range= в функции read_excel()это читает большую часть таблицы.

ОБНОВЛЕНИЕ: чтение нескольких кварталов с определенного листа

29 ноября оригинальный постер изменил вопрос, объяснив, что в файле Excel было несколько листов, по одному на каждый год.Это легко сделать с помощью следующих модификаций:

  1. Укажите рабочий лист с параметром sheet=
  2. Добавьте _Q1, чтобы различать чтение каждого квартала и сохранить квартал в качестве ключа.переменная
  3. Установить имена рабочих листов в годы

Полученные в результате аккуратные данные будут иметь столбцы года и квартала.Обратите внимание, что я обновил свою рабочую книгу Excel фиктивными данными, чтобы рабочие таблицы, представляющие разные годы, имели разные данные, чтобы результаты были различимыми.

# download file from github to make script completely reproducible

sourceFile <- "https://raw.githubusercontent.com/lgreski/stackoverflowanswers/master/data/soQuestion53446800.xlsx"
destinationFile <- "./soQuestion53446800.xlsx"
download.file(sourceFile,destinationFile,mode="wb")

# set constants 
typeOfLeave <- "sick"
group <- "self employed"
year <- "2018"

# setup column names using underscore so we can separate key column into Sex, Age, and Quarter columns 
# after using rep() to build data with required repeating patterns, avoiding manual typing of all the column names 
sex <- rep(c("both","women","men"),16)
age <- rep(c(rep("all",3),rep("up to 17",3),rep("18 to 64",3),rep("65 and over",3)),4)
quarter <- c(rep("Q1",12),rep("Q2",12),rep("Q3",12),rep("Q4",12))
data.frame(sex,age,quarter) %>% unite(excelColNames) -> columnsData
theCols <- unlist(c("Country",columnsData["excelColNames"]))

theData <- read_excel(destinationFile,sheet=year,range="A5:AW9",col_names=theCols)

# use tidyr / dplyr to transform the data
theData %>% gather(.,key="key",value="Amount",2:49) %>% separate(.,key,into=c("Sex","Age","Quarter"),sep="_") -> tidyData

# assign constants

tidyData$typeOfLeave <- typeOfLeave
tidyData$group <- group
tidyData$year <- year

tidyData

... и выходные данные, считанные из листа 2018 в рабочей книге.

> tidyData
# A tibble: 240 x 8
   Country    Sex   Age   Quarter Amount typeOfLeave group         year 
   <chr>      <chr> <chr> <chr>    <dbl> <chr>       <chr>         <chr>
 1 Total      both  all   Q1        2100 sick        self employed 2018 
 2 Afganistan both  all   Q1        2100 sick        self employed 2018 
 3 Albania    both  all   Q1        2100 sick        self employed 2018 
 4 Algeria    both  all   Q1        2100 sick        self employed 2018 
 5 Andora     both  all   Q1        2100 sick        self employed 2018 
 6 Total      women all   Q1         900 sick        self employed 2018 
 7 Afganistan women all   Q1         900 sick        self employed 2018 
 8 Albania    women all   Q1         900 sick        self employed 2018 
 9 Algeria    women all   Q1         900 sick        self employed 2018 
10 Andora     women all   Q1         900 sick        self employed 2018 
# ... with 230 more rows
> 

Если мы изменим параметры конфигурации, мы сможем прочитать данные за 2017 год из рабочей книги Iотправил на Github.

# read second worksheet to illustrate multiple reads 

# set constants 
typeOfLeave <- "sick"
group <- "self employed"
year <- "2017"

theData <- read_excel(destinationFile,sheet=year,range="A5:AW9",col_names=theCols)

# use tidyr / dplyr to transform the data
theData %>% gather(.,key="key",value="Amount",2:49) %>% separate(.,key,into=c("Sex","Age","Quarter"),sep="_") -> tidyData

# assign constants

tidyData$typeOfLeave <- typeOfLeave
tidyData$group <- group
tidyData$year <- year

tidyData

... и вывод:

> tidyData
# A tibble: 240 x 8
   Country    Sex   Age   Quarter Amount typeOfLeave group         year 
   <chr>      <chr> <chr> <chr>    <dbl> <chr>       <chr>         <chr>
 1 Total      both  all   Q1       33000 sick        self employed 2017 
 2 Afganistan both  all   Q1       33000 sick        self employed 2017 
 3 Albania    both  all   Q1       33000 sick        self employed 2017 
 4 Algeria    both  all   Q1       33000 sick        self employed 2017 
 5 Andora     both  all   Q1       33000 sick        self employed 2017 
 6 Total      women all   Q1       15000 sick        self employed 2017 
 7 Afganistan women all   Q1       15000 sick        self employed 2017 
 8 Albania    women all   Q1       15000 sick        self employed 2017 
 9 Algeria    women all   Q1       15000 sick        self employed 2017 
10 Andora     women all   Q1       15000 sick        self employed 2017 
# ... with 230 more rows
> 

Собираем все вместе ...

На этом этапе мы встроили основные идеи в сценарийэто полностью читает один лист.Если мы немного изменим код и включим такую ​​функцию, как lapply(), мы можем начать с вектора имен рабочих листов, прочитать файлы, преобразовать их в формат аккуратных данных и объединить файлы в один набор аккуратных данных с помощью do.call() и rbind().

## version that combines multiple years into a single narrow format tidy data file
# download file from github to make script completely reproducible

sourceFile <- "https://raw.githubusercontent.com/lgreski/stackoverflowanswers/master/data/soQuestion53446800.xlsx"
destinationFile <- "./soQuestion53446800.xlsx"
download.file(sourceFile,destinationFile,mode="wb")


library(readxl)
library(tidyr)

# set constants
years <- c("2017","2018")
typeOfLeave <- "sick"
group <- "self employed"

# setup column names using underscore so we can separate key column into Sex, Age, and Quarter columns 
# after using rep() to build data with required repeating patterns, avoiding manual typing of all the column names 
sex <- rep(c("both","women","men"),16)
age <- rep(c(rep("all",3),rep("up to 17",3),rep("18 to 64",3),rep("65 and over",3)),4)
quarter <- c(rep("Q1",12),rep("Q2",12),rep("Q3",12),rep("Q4",12))
data.frame(sex,age,quarter) %>% unite(excelColNames) -> columnsData
theCols <- unlist(c("Country",columnsData["excelColNames"]))


lapply(years,function(x){
  theData <- read_excel(destinationFile,sheet=x,range="A5:AW9",col_names=theCols)

  # use tidyr / dplyr to transform the data
  theData %>% gather(.,key="key",value="Amount",2:49) %>% separate(.,key,into=c("Sex","Age","Quarter"),sep="_") -> tidyData

  # assign constants

  tidyData$typeOfLeave <- typeOfLeave
  tidyData$group <- group
  tidyData$year <- x

  tidyData
}) %>% do.call(rbind,.) -> combinedData 

... и выходные данные, демонстрирующие, что фрейм данных combinedData содержит данные рабочих таблиц как 2017, так и 2018 года.

> head(combinedData)
# A tibble: 6 x 8
  Country    Sex   Age   Quarter Amount typeOfLeave group         year 
  <chr>      <chr> <chr> <chr>    <dbl> <chr>       <chr>         <chr>
1 Total      both  all   Q1       33000 sick        self employed 2017 
2 Afganistan both  all   Q1       33000 sick        self employed 2017 
3 Albania    both  all   Q1       33000 sick        self employed 2017 
4 Algeria    both  all   Q1       33000 sick        self employed 2017 
5 Andora     both  all   Q1       33000 sick        self employed 2017 
6 Total      women all   Q1       15000 sick        self employed 2017 
> tail(combinedData)
# A tibble: 6 x 8
  Country    Sex   Age         Quarter Amount typeOfLeave group         year 
  <chr>      <chr> <chr>       <chr>    <dbl> <chr>       <chr>         <chr>
1 Andora     women 65 and over Q4        2300 sick        self employed 2018 
2 Total      men   65 and over Q4        2400 sick        self employed 2018 
3 Afganistan men   65 and over Q4        2400 sick        self employed 2018 
4 Albania    men   65 and over Q4        2400 sick        self employed 2018 
5 Algeria    men   65 and over Q4        2400 sick        self employed 2018 
6 Andora     men   65 and over Q4        2400 sick        self employed 2018 
>
...