本手册强调使用tidyverse R 包系列中的函数。下面列出了本页中演示的基本 R 函数。
其中许多函数属于dplyr R 包,它提供“verb”函数来解决数据操作挑战(名称是对“data frame-plier”)的引用。dplyr是tidyverse R 包系列的一部分(其中还包括ggplot2、tidyr、stringr、tibble、purrr、magrittr和forcats等)。
Function | Utility | Package |
---|---|---|
%>% | “pipe” (pass) data from one function to the next | magrittr |
mutate() | create, transform, and re-define columns | dplyr |
select() | keep, remove, select, or re-name columns | dplyr |
rename() | rename columns | dplyr |
clean_names() | standardize the syntax of column names | janitor |
as.character() , as.numeric() , as.Date() , etc. | convert the class of a column | base R |
across() | transform multiple columns at one time | dplyr |
tidyselect functions | use logic to select columns | tidyselect |
filter() | keep certain rows | dplyr |
distinct() | de-duplicate rows | dplyr |
rowwise() | operations by/within each row | dplyr |
add_row() | add rows manually | tibble |
arrange() | sort rows | dplyr |
recode() | re-code values in a column | dplyr |
case_when() | re-code values in a column using more complex logical criteria | dplyr |
replace_na() , na_if() , coalesce() | special functions for re-coding | tidyr |
age_categories() and cut() | create categorical groups from a numeric column | epikit and base R |
clean_variable_spelling() | re-code/clean values using a data dictionary | linelist |
which() | apply logical criteria; return indices | base R |
在本手册中,我们通常引用“列”和“行”而不是“变量”和“观察值”。正如本“tidy data”入门所解释的,大多数流行病学统计数据集在结构上由行、列和值组成。
变量包含衡量相同基础属性(如年龄组、结果或发病日期)的值。观测值包含在同一单位(例如人、地点或实验室样本)上测量的所有值。因此,这些方面可能更难以有形地定义。
在“整洁”的数据集中,每一列是一个变量,每一行是一个观察值,每个单元格是一个值。但是,您遇到的一些数据集不适合这种模式 - “宽”格式数据集可能有一个变量拆分为多个列(请参阅透视数据页面中的示例)。同样,观察可以分成几行。
Cleaning pipeline
在流行病学分析和数据处理中,清洗步骤通常是按顺序执行的,相互关联。在 R 中,这通常表现为一个清理“管道”,其中原始数据集从一个清理步骤传递或“管道”到另一个清理步骤。
这样的链使用dplyr “动词”函数和magrittr管道运算符%>%
。该管道以“原始”数据(“linelist_raw.xlsx”)开始,以linelist
可以使用、保存、导出等的“干净”R 数据帧 ()结束。
在清洗管道中,步骤的顺序很重要。清洁步骤可能包括:
- 数据导入
- 列名已清理或更改
- 重复数据删除
- 列创建和转换(例如重新编码或标准化值)
- 过滤或添加的行
加载包
此代码块显示分析所需的包的加载。在本手册中,我们强调p_load()
来自pacman,它会在必要时安装包并加载它以供使用。
pacman::p_load(
rio, # importing data
here, # relative file pathways
janitor, # data cleaning and tables
lubridate, # working with dates
epikit, # age_categories() function
tidyverse # data management and visualization
)
导入数据
import
,我们使用包rio中的import()函数导入“raw” case linelist Excel 文件。rio包可以灵活处理多种类型的文件(例如 .xlsx、.csv、.tsv、.rds。有关异常情况的更多信息和提示,请参见导入和导出页面(例如,跳过行、设置缺失值、导入 Google 表格) , 等等)。
如果您想继续,请单击以下载“原始”行列表(作为 .xlsx 文件)。
如果您的数据集很大并且需要很长时间才能导入,那么将导入命令与管道链分开并将“raw”保存为不同的文件会很有用。这也允许在原始版本和清洁版本之间进行轻松比较。
下面我们导入原始 Excel 文件并将其保存为数据框linelist_raw
。我们假设该文件位于您的工作目录或 R 项目根目录中,因此文件路径中未指定子文件夹。
linelist_raw <- import("linelist_raw.xlsx")
Review
您可以使用包skimr中的skim()
函数来获取整个数据框的概述(有关详细信息,请参阅描述性表页面)。列按类/类型进行汇总,例如字符、数字。注意:“POSIXct”是一种原始日期类(请参阅使用日期。
skimr::skim(linelist_raw)
列名
在 R 中,列名是列的“header”或“top”值。它们用于引用代码中的列,并用作图中的默认标签。
由于 R 列名称经常使用,因此它们必须具有“干净”的语法。我们建议如下:
- 简称
- 没有空格(替换为下划线 _ )
- 没有异常字符(&、#、<、>、...)
- 类似风格的命名法(例如,所有日期列都命名为date_onset、date_report、date_death ……)
下面使用names()
打印出linelist_raw的
列名。我们最初可以看到:
- 一些名称包含空格(例如
infection date
) - 不同的命名模式用于日期(
date onset
vs.infection date
) - .xlsx 中的最后两列必须有一个合并的标题。我们知道这一点是因为两个合并列的名称(“merged_header”)由 R 分配给第一列,第二列分配了一个占位符名称“…28”(因为它当时是空的,是第 28 列)。
names(linelist_raw)
## [1] "case_id" "generation" "infection date" "date onset" "hosp date" "date_of_outcome" "outcome" "gender"
## [9] "hospital" "lon" "lat" "infector" "source" "age" "age_unit" "row_num"
## [17] "wt_kg" "ht_cm" "ct_blood" "fever" "chills" "cough" "aches" "vomit"
## [25] "temp" "time_admission" "merged_header" "...28"
注意:要引用包含空格的列名,请用反引号将名称括起来,例如: linelist$` '\x60infection date\x60'`
。请注意,在您的键盘上,反引号 (`) 与单引号 (') 不同。
Automatic cleaning
janitor包中的clean_names()
函数标准化列名并通过执行以下操作使它们唯一:
- 将所有名称转换为仅包含下划线、数字和字母
- 重音字符被音译为 ASCII(例如,带有变音符号的德语 o 变为“o”,西班牙语“enye”变为“n”)
- 可以使用
case =
参数指定新列名称的大写首选项(“snake”是默认值,替代方案包括“sentence”、“title”、“small_camel”...) - 您可以通过为参数提供一个向量来指定特定的名称替换
replace =
(例如replace = c(onset = "date_of_onset")
)
下面,清洁管道从使用clean_names()
开始。
# pipe the raw dataset through the function clean_names(), assign result as "linelist"
linelist <- linelist_raw %>%
janitor::clean_names()
# see the new column names
names(linelist)
## [1] "case_id" "generation" "infection_date" "date_onset" "hosp_date" "date_of_outcome" "outcome" "gender"
## [9] "hospital" "lon" "lat" "infector" "source" "age" "age_unit" "row_num"
## [17] "wt_kg" "ht_cm" "ct_blood" "fever" "chills" "cough" "aches" "vomit"
## [25] "temp" "time_admission" "merged_header" "x28"
注意:最后一列名称“…28”已更改为“x28”。
Manual name cleaning
即使在上述标准化步骤之后,通常也需要手动重命名列。下面,使用dplyrrename()
包中的rename()
函数执行重命名,作为管道链的一部分。rename()使用样式NEW = OLD
- 新列名在旧列名之前给出。rename()
下面,将重命名命令添加到清理管道中。战略性地添加了空格以对齐代码以便于阅读。
# CLEANING 'PIPE' CHAIN (starts with raw data and pipes it through cleaning steps)
##################################################################################
linelist <- linelist_raw %>%
# standardize column name syntax
janitor::clean_names() %>%
# manually re-name columns
# NEW name # OLD name
rename(date_infection = infection_date,
date_hospitalisation = hosp_date,
date_outcome = date_of_outcome)
## [1] "case_id" "generation" "date_infection" "date_onset" "date_hospitalisation" "date_outcome"
## [7] "outcome" "gender" "hospital" "lon" "lat" "infector"
## [13] "source" "age" "age_unit" "row_num" "wt_kg" "ht_cm"
## [19] "ct_blood" "fever" "chills" "cough" "aches" "vomit"
## [25] "temp" "time_admission" "merged_header" "x28"
您还可以按列位置重命名,而不是按列名,例如:
rename(newNameForFirstColumn = 1,
newNameForSecondColumn = 2)
通过select()
和summarise()
重命名
作为快捷方式,您还可以使用dplyr包的 select()
和summarise()
函数重命名列。select()
用于仅保留某些列(本页稍后会介绍)。summarise()
在分组数据和描述表页面中进行了介绍。这些函数也使用格式new_name = old_name
。这是一个例子:
linelist_raw %>%
select(# NEW name # OLD name
date_infection = `infection date`, # rename and KEEP ONLY these columns
date_hospitalisation = `hosp date`)
其他挑战
空 Excel 列名
R 不能有没有列名(标题)的数据集列。因此,如果您导入包含数据但没有列标题的 Excel 数据集,R 将使用“…1”或“…2”等名称填充标题。数字代表列号(例如,如果数据集中的第 4 列没有标题,则 R 将其命名为“…4”)。
您可以通过引用它们的位置编号(参见上面的示例)或它们指定的名称 ( linelist_raw$...1
) 来手动清除这些名称。
合并 Excel 列名和单元格
Excel 文件中的合并单元格在接收数据时很常见。正如在过渡到 R中所解释的,合并的单元格对于人类读取数据可能很好,但不是“整洁的数据”,并且会给机器读取数据带来很多问题。R 不能容纳合并的单元格。
提醒进行数据录入的人,人类可读的数据与机器可读的数据不同。努力对用户进行整理数据原则的培训。如果可能,请尝试更改程序,以使数据以整洁的格式到达,而不会合并单元格。
- 每个变量必须有自己的列。
- 每个观察必须有自己的行。
- 每个值都必须有自己的单元格。
使用rio的import()
功能时,合并单元格中的值将分配给第一个单元格,后续单元格将为空。
处理合并单元格的一种解决方案是使用openxlsx包中的readWorkbook()
函数导入数据。设置参数fillMergedCells = TRUE
。这将合并单元格中的值提供给合并范围内的所有单元格。
linelist_raw <- openxlsx::readWorkbook("linelist_raw.xlsx", fillMergedCells = TRUE)
危险:如果列名与 合并readWorkbook()
,您最终会得到重复的列名,您需要手动修复 - R 不能很好地处理重复的列名!您可以通过引用它们的位置来重新命名它们(例如第 5 列),如手动列名清理部分所述。
未完待续。。。