我对应用于SQL等关系数据库的数据归一化技术有一些认识。https://en.wikipedia.org/wiki/database_normalization
我对将这些技术应用于从各种来源获得的平面文件(CSV(非常感兴趣。
是否有任何特定的软件包或技术有助于获得功能多余的列并有助于重新装修我的数据?可能找到所有相互依存的变量(也许完全相关?(,将它们剥离并将其复制为查找表。
r首选解决方案,因为这是我们团队的主要工具,但是Python也可以。
example
| Product | Class | Class 2 | Sales |
| A | Red | Large | 12 |
| A | Red | Large | 42 |
| B | Blue | Small | 12 |
让产品始终为红色且大。B总是蓝色和小。因此,一种更好的存储数据的方法是:
Table 1 Lookup Table
| A | 12 | | A | Red | Large |
| A | 42 | | B | Blue | Small |
| B | 12 |
这是一个相当广泛的问题,也邀请了自以为是的答案。
但是,我的答案包括四个部分:
- 读取数据文件
- 识别冗余列
- 分开数据
- 在需要时组合(加入,合并(
虽然另一个答案已经显示了如何使用base r进行步骤3,并且我建议在处理大量数据时出于性能原因使用其他替代方案(sqldf
,tidyverse
(。
步骤1:读取数据
在这里,我们模拟了来自字符串的读取数据,但fread()
也将读取文件:
DT <- fread(
"| Product | Class | Class_2 | Sales |
| A | Red | Large | 12 |
| A | Red | Large | 42 |
| B | Blue | Small | 12 |
| B | Blue | Small | 22 |
| C | Blue | Large | 59 |
",
sep = "|"
)[, Product:Sales]
DT
# Product Class Class_2 Sales
#1: A Red Large 12
#2: A Red Large 42
#3: B Blue Small 12
#4: B Blue Small 22
#5: C Blue Large 59
请注意,添加了行W.R.T.OP的样本数据以示例步骤2。
步骤2:识别冗余列
归一化是一项复杂的设计任务。但是,它可以通过对唯一值的分析来指导:
DT[, lapply(.SD, uniqueN)]
# Product Class Class_2 Sales
#1: 3 2 2 4
如果 redunancy的测量为重复值的百分比这将变为:
DT[, lapply(.SD, function(x) 100 * (1 - uniqueN(x) / .N))]
# Product Class Class_2 Sales
#1: 40 60 60 20
Class
和 Class_2
是重复值最高份额的列,而 Sales
的冗余最低。
看列时,图片变得更清晰:
rbindlist(
lapply(
combn(names(DT), 2L, simplify = FALSE),
function(cols) data.table(t(cols),
N = uniqueN(DT, by = cols))
)
)[order(N, V1, V2)][, redundancy := 100 * (1 - N / nrow(DT))][]
# V1 V2 N redundancy
#1: Class Class_2 3 40
#2: Product Class 3 40
#3: Product Class_2 3 40
#4: Class Sales 5 0
#5: Class_2 Sales 5 0
#6: Product Sales 5 0
因此,为了减少整体恢复,可以在包含Class
和Class_2
的一个产品表中分配数据,以及第二个 sales Table Product
CC_11 作为两个表中的钥匙列。
注意,这是一种临时方法,可用于此简单示例。对于带有许多列的大桌子可能会有所不同。
步骤3:分开数据
创建两个表如下
PT <- unique(DT[, -"Sales"])
PT
# Product Class Class_2
#1: A Red Large
#2: B Blue Small
#3: C Blue Large
ST <- DT[, c("Product", "Sales")]
ST
# Product Sales
#1: A 12
#2: A 42
#3: B 12
#4: B 22
#5: C 59
请注意,ST
不能被删除,因为它可能包含单个交易。
步骤4:在需要时组合数据
现在,可以使用ST
没有冗余信息,例如,用于聚合:
ST[, .(total_sales = sum(Sales)), by = Product]
# Product total_sales
#1: A 54
#2: B 34
#3: C 59
如果汇总结果应包括每个产品的基本数据,则可以使用Product
作为键加入:
ST[, .(total_sales = sum(Sales)), by = Product][PT, on = "Product"]
# Product total_sales Class Class_2
#1: A 54 Red Large
#2: B 34 Blue Small
#3: C 59 Blue Large
如果请求所有Blue
产品的销售数字,则必须首先过滤PT
,然后与ST
连接,不包括在内,然后不匹配行:
PT[Class == "Blue"][ST, on = "Product", nomatch = 0]
# Product Class Class_2 Sales
#1: B Blue Small 12
#2: B Blue Small 22
#3: C Blue Large 59
如果要显示的Blue
最高的CC_18产品可以通过:
PT[Class == "Blue"][ST, on = "Product", nomatch = 0
][, .(total_sales = sum(Sales)), by = .(Product, Class)
][order(-total_sales)]
# Product Class total_sales
#1: C Blue 59
#2: B Blue 34
这给出了R:
的所需结果df <- read.table(header=TRUE, sep="|", text=
'| Product | Class | Class 2 | Sales |
| A | Red | Large | 12 |
| A | Red | Large | 42 |
| B | Blue | Small | 12 |')[-c(1,6)]
unique(df[1:3])
# > unique(df[1:3])
# Product Class Class.2
# 1 A Red Large
# 3 B Blue Small
df[,c("Product", "Sales")]
# > df[,c("Product", "Sales")]
# Product Sales
# 1 A 12
# 2 A 42
# 3 B 12