Q&A:在melt
和dcast
之间反复横跳
写在前面
各位水友大家好,自从上一次发布了改版的推文说明之后,大喵和村长收到了很多水友的问题,我们也对这些问题进行了回复,希望能对大家R语言的学习有所帮助,在此先谢谢各位的支持!本期我们精心挑选了一位水友遇到的问题进行知识分享,希望大家踊跃提问,在此再次谢过了!
收到的问题
首先感谢我不是黄欢乐的提问。在处理数据的过程中可能会遇到这种情况:许多数据记录存在横向和纵向不明确的情况。在如下数据集中,第1个姓名id横向呈现了3次用药记录,第2个姓名id在纵向呈现了4次用药记录,且存在两次空记录。
代码语言:javascript复制library(data.table)
data <- fread("data.txt", encoding = "UTF-8", na.string = "")
data[1:5]
姓名 | 用药名称1 | 用法1 | 用量1 | 服药时间1 | 用药依从性1 | 用药名称2 | 用法2 | 用量2 | 服药时间2 | 用药依从性2 | 用药名称3 | 用法3 | 用量3 | 服药时间3 | 用药依从性3 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
梁小妹 | 缬沙坦胶囊 | 口服 | 80mg/qd | 1年 | 规律 | 甲磺酸氨氯地平片 | 口服 | 5mg/qd | 1年 | 规律 | 阿司匹林肠溶片 | 口服 | 100mg/qd | 1年 | 规律 |
郑浮昌 | 厄贝沙坦 | 口服,每日1次。 | 150mg | 1年 | 规律 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
郑浮昌 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
郑浮昌 | 硫酸氢氯比格雷片 | 口服,每日1次。 | 25mg | 1年 | 规律 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
郑浮昌 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
最终对数据集的清洗结果:需要使得每个姓名id只存在一行记录,所有的记录横向排列,并且需要删除所有的含NA记录的项。 ”
问题解决
我们照惯例先把这段代码优雅的放上来,再细细解读:
代码语言:javascript复制data <- data[, melt(.SD, measure = patterns("^用药名称", "^用法", "^用量", "^服药时间", "^用药依从性"), value.name = c("用药名称", "用法", "用量", "服药时间", "用药依从性"))]
data <- data[rowMeans(!is.na(data[, 3:7])) == 1, .SD
][, variable := 1:.N, by = .(`姓名`)
][, dcast(.SD, `姓名` ~ variable, value.var = c("用药名称", "用法", "用量", "服药时间", "用药依从性"))]
data
姓名 | 用药名称_1 | 用药名称_2 | 用药名称_3 | 用法_1 | 用法_2 | 用法_3 | 用量_1 | 用量_2 | 用量_3 | 服药时间_1 | 服药时间_2 | 服药时间_3 | 用药依从性_1 | 用药依从性_2 | 用药依从性_3 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
叶桃芳 | 安耐喜 | NA | NA | 口服 | NA | NA | qd,1粒 | NA | NA | 1年 | NA | NA | 规律 | NA | NA |
庞燕改 | 厄贝沙坦 | 硫酸氢氯比格雷片 | 珍菊降压片 | 口服,每日1次。 | 口服,每日1次。 | 口服,每日1次。 | 150mg | 25mg | 1片 | 1年 | 1年 | 1年 | 规律 | 规律 | 规律 |
林丽芳 | 福辛普利钠片 | NA | NA | 口服 | NA | NA | 10mg | NA | NA | 1年 | NA | NA | 规律 | NA | NA |
梁小妹 | 缬沙坦胶囊 | 甲磺酸氨氯地平片 | 阿司匹林肠溶片 | 口服 | 口服 | 口服 | 80mg/qd | 5mg/qd | 100mg/qd | 1年 | 1年 | 1年 | 规律 | 规律 | 规律 |
罗秀丽 | 甲磺酸氨氯地平片 | NA | NA | 口服 | NA | NA | 1粒 qd | NA | NA | 1年 | NA | NA | 规律 | NA | NA |
郑浮昌 | 厄贝沙坦 | 硫酸氢氯比格雷片 | 珍菊降压片 | 口服,每日1次。 | 口服,每日1次。 | 口服,每日1次。 | 150mg | 25mg | 1片 | 1年 | 1年 | 1年 | 规律 | 规律 | 规律 |
黄舜 | 盐酸吡格列酮(卡司平) | NA | NA | 口服 | NA | NA | 2片qd | NA | NA | 1年 | NA | NA | 规律 | NA | NA |
代码解读
首先来看第一部分代码:
代码语言:javascript复制data <- data[, melt(.SD, measure = patterns("^用药名称", "^用法", "^用量", "^服药时间", "^用药依从性"), value.name = c("用药名称", "用法", "用量", "服药时间", "用药依从性"))]
data[1:5]
姓名 | variable | 用药名称 | 用法 | 用量 | 服药时间 | 用药依从性 |
---|---|---|---|---|---|---|
梁小妹 | 1 | 缬沙坦胶囊 | 口服 | 80mg/qd | 1年 | 规律 |
郑浮昌 | 1 | 厄贝沙坦 | 口服,每日1次。 | 150mg | 1年 | 规律 |
郑浮昌 | 1 | NA | NA | NA | NA | NA |
郑浮昌 | 1 | 硫酸氢氯比格雷片 | 口服,每日1次。 | 25mg | 1年 | 规律 |
郑浮昌 | 1 | NA | NA | NA | NA | NA |
在这里我们利用了melt
这样一个函数。利用这个函数的目的在于,在data.table中进行数据处理贯彻的是向量思维。
这也是R语言和Python语言进行数据处理的底层逻辑。从数据特点的角度来解释,也即是长表优于宽表。 ”
有鉴于此,必须首先想办法把变量减少,使得宽表变成长表,而更有利于之后的操作。
通过使用melt
能够达到这一效果,在这里使用了melt
中的measure选项,通过patterns进行了关于变量名的正则匹配,将五类同属性变量("^用药名称", "^用法", "^用量", "^服药时间", "^用药依从性")
进行合并;并最终通过value.name的选项对五个统一后的变量进行重命名。
第二部分代码我们分成两部分来看:
代码语言:javascript复制data <- data[rowMeans(!is.na(data[, 3:7])) == 1, .SD
][, variable := 1:.N, by = .(`姓名`)]
data[1:5]
姓名 | variable | 用药名称 | 用法 | 用量 | 服药时间 | 用药依从性 |
---|---|---|---|---|---|---|
梁小妹 | 1 | 缬沙坦胶囊 | 口服 | 80mg/qd | 1年 | 规律 |
郑浮昌 | 1 | 厄贝沙坦 | 口服,每日1次。 | 150mg | 1年 | 规律 |
郑浮昌 | 2 | 硫酸氢氯比格雷片 | 口服,每日1次。 | 25mg | 1年 | 规律 |
郑浮昌 | 3 | 珍菊降压片 | 口服,每日1次。 | 1片 | 1年 | 规律 |
黄舜 | 1 | 盐酸吡格列酮(卡司平) | 口服 | 2片qd | 1年 | 规律 |
这一部分代码极为重要,首先利用rowMeans
进行行筛选,为的是将原本就缺失的记录,以及在宽表到长表转换中生成的缺失记录进行删除。这就是源于数据的横向与纵向记录规则不明确导致的,在两个方向都可能会存在缺失值。在进行宽表到长表的转化过程中,这样的缺失值同样会保留下来。因此要对数据进行该操作。此外关于函数筛选的用法,这里不进行阐述,关于这内容的详细解读可参考R语言:以多列标准筛选特定行。
此外对variable
这个变量进行了更改。由于之后需要将长表变成宽表,因此需要对每一个姓名id的所有不同记录进行编号。可以发现经过melt
之后的数据,编号依据是曾经的观测记录。现在数据的观测记录发生了改变,因此需要对观测记录进行重新编号。在这里利用了1:.N
进行by
的分组编号。
最后一部分代码则为melt
的逆操作:
data <- data[, dcast(.SD, `姓名` ~ variable, value.var = c("用药名称", "用法", "用量", "服药时间", "用药依从性"))]
data
姓名 | 用药名称_1 | 用药名称_2 | 用药名称_3 | 用法_1 | 用法_2 | 用法_3 | 用量_1 | 用量_2 | 用量_3 | 服药时间_1 | 服药时间_2 | 服药时间_3 | 用药依从性_1 | 用药依从性_2 | 用药依从性_3 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
叶桃芳 | 安耐喜 | NA | NA | 口服 | NA | NA | qd,1粒 | NA | NA | 1年 | NA | NA | 规律 | NA | NA |
庞燕改 | 厄贝沙坦 | 硫酸氢氯比格雷片 | 珍菊降压片 | 口服,每日1次。 | 口服,每日1次。 | 口服,每日1次。 | 150mg | 25mg | 1片 | 1年 | 1年 | 1年 | 规律 | 规律 | 规律 |
林丽芳 | 福辛普利钠片 | NA | NA | 口服 | NA | NA | 10mg | NA | NA | 1年 | NA | NA | 规律 | NA | NA |
梁小妹 | 缬沙坦胶囊 | 甲磺酸氨氯地平片 | 阿司匹林肠溶片 | 口服 | 口服 | 口服 | 80mg/qd | 5mg/qd | 100mg/qd | 1年 | 1年 | 1年 | 规律 | 规律 | 规律 |
罗秀丽 | 甲磺酸氨氯地平片 | NA | NA | 口服 | NA | NA | 1粒 qd | NA | NA | 1年 | NA | NA | 规律 | NA | NA |
郑浮昌 | 厄贝沙坦 | 硫酸氢氯比格雷片 | 珍菊降压片 | 口服,每日1次。 | 口服,每日1次。 | 口服,每日1次。 | 150mg | 25mg | 1片 | 1年 | 1年 | 1年 | 规律 | 规律 | 规律 |
黄舜 | 盐酸吡格列酮(卡司平) | NA | NA | 口服 | NA | NA | 2片qd | NA | NA | 1年 | NA | NA | 规律 | NA | NA |
在运用dcast
时,~
左边的变量为表更改结构以后体现记录识别唯一性的primary key,~
右边的变量为数据变宽之后同类记录的序号variable
,value.var
中的变量名与~
右边变量中记录的序号整合在一起生成一系列的同类变量,最终得到我们想要的结果。
总结
该问题最主要考察了对数据结构的理解,如何在记录规则混乱的情况下,进行数据结构化处理。长表和宽表之间的相互转换,有时会在数据清洗中用到,对melt
和dcast
两个函数的理解需要深入。