R-应用流行病学和公共卫生-7.数据清洗

本手册强调使用tidyverse R 包系列中的函数。下面列出了本页中演示的基本 R 函数。

其中许多函数属于dplyr R 包,它提供“verb”函数来解决数据操作挑战(名称是对“data frame-plier”)的引用。dplyrtidyverse R 包系列的一部分(其中还包括ggplot2tidyrstringrtibblepurrrmagrittrforcats等)。

FunctionUtilityPackage
%>%“pipe” (pass) data from one function to the nextmagrittr
mutate()create, transform, and re-define columnsdplyr
select()keep, remove, select, or re-name columnsdplyr
rename()rename columnsdplyr
clean_names()standardize the syntax of column namesjanitor
as.character()as.numeric()as.Date(), etc.convert the class of a columnbase R
across()transform multiple columns at one timedplyr
tidyselect functionsuse logic to select columnstidyselect
filter()keep certain rowsdplyr
distinct()de-duplicate rowsdplyr
rowwise()operations by/within each rowdplyr
add_row()add rows manuallytibble
arrange()sort rowsdplyr
recode()re-code values in a columndplyr
case_when()re-code values in a column using more complex logical criteriadplyr
replace_na()na_if()coalesce()special functions for re-codingtidyr
age_categories() and cut()create categorical groups from a numeric columnepikit and base R
clean_variable_spelling()re-code/clean values using a data dictionarylinelist
which()apply logical criteria; return indicesbase 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_onsetdate_reportdate_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 不能容纳合并的单元格。

提醒进行数据录入的人,人类可读的数据与机器可读的数据不同。努力对用户进行整理数据原则的培训。如果可能,请尝试更改程序,以使数据以整洁的格式到达,而不会合并单元格。

  • 每个变量必须有自己的列。
  • 每个观察必须有自己的行。
  • 每个值都必须有自己的单元格。

使用rioimport()功能时,合并单元格中的值将分配给第一个单元格,后续单元格将为空。

处理合并单元格的一种解决方案是使用openxlsx包中的readWorkbook()函数导入数据。设置参数fillMergedCells = TRUE。这将合并单元格中的值提供给合并范围内的所有单元格。

linelist_raw <- openxlsx::readWorkbook("linelist_raw.xlsx", fillMergedCells = TRUE)

危险:如果列名与 合并readWorkbook(),您最终会得到重复的列名,您需要手动修复 - R 不能很好地处理重复的列名!您可以通过引用它们的位置来重新命名它们(例如第 5 列),如手动列名清理部分所述。

未完待续。。。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值