data.table 与 pandas

🕓2019年12月26日 · ☕4 分钟

常见的数据分析项目过程包括加载数据-清洗数据-(特征处理、可视化、模型模拟)-交流成果。其中,清洗数据与特征处理一般占据整个项目的大部分时间。熟练掌握相关工具,提高数据处理的效率,是开展数据分析工作的基础。

数据框(data frame)是我们接触最多的数据形式,其每一列都是向量且长度一致。在 R 中我通常使用 data.table 包对数据框进行处理分析,而在 python 环境中 pandas 最为常用的。根据 Database-like ops benchmark 显示,data.table 在大部分任务中性能表现最好,而且其语法也相对简洁统一。为了方便大家对比学习,本文将分别用 data.tablepandas 实现常见的数据分析任务12

探索数据

读取 csv 文件

library(data.table)
packageVersion('data.table')
## [1] '1.12.8'
url = "https://vincentarelbundock.github.io/Rdatasets/csv/datasets/HairEyeColor.csv"
dt = fread(url)
import pandas as pd
pd.__version__
## '0.25.3'
url = "https://vincentarelbundock.github.io/Rdatasets/csv/datasets/HairEyeColor.csv"
df = pd.read_csv(url)

查看数据结构

#数据类型
class(dt)
str(dt)

# 列名
names(dt)
# 打印前后几行
head(dt, n=3)
tail(dt, n=3)

# 维度
dim(dt)
nrow(dt)
ncol(dt)

# 汇总
summary(dt)
#数据类型
type(df)
df.dtypes

# 列名
list(df)
# 打印前后几行
df.head(n=3)
df.tail(n=3)

# 维度
df.shape
len(df.index)
len(df.columns)

# 汇总
df.describe()

行选择与排序

rows filter

# 基于行所在位置筛选
dt[c(3,1,2)]


# 单条件筛选
dt[Hair == 'Red']

# 多条件筛选
dt[Hair == 'Black' & 
   Freq >= 10 & 
   Eye %in% c('Brown', 'Blue')]
# 基于行所在位置筛选
df.iloc[[2,0,1]] # python序数从0开始,2代表第三行
df.loc[[2,0,1]] # 如果index未修改,效果与iloc的一致

# 单条件筛选,去掉.loc效果一致
df.loc[df['Hair'] == 'Red'] 

# pandas 多条件筛选时要用 |, &, ~分别代表or, and, not; 
# 且每个条件用括号区分 
df.loc[(df['Hair'] == 'Black') & 
       (df['Freq'] >= 10) & 
       (df['Eye'].isin(['Brown', 'Blue']))]

rows arrange

dt[order(Sex, -Freq)]
df.sort_values(
  ['Sex', 'Freq'], 
  ascending = [True, False] )

列选择、新建与计算

columns select

dt[,.(Hair, Freq)]
# or 
dt[,c('Eye', 'Sex'), with=FALSE]
df[['Hair', 'Freq']]
# or 
df.loc[:,['Eye', 'Sex']] # 选一列时也要保留[],否则与df.Eye一样为series

columns mutate

# 新建一列
dt[, nc := .I] # .I .N .SD为特殊符号,查看帮助?`.I`
dt[,'nc0'] = 1:32

# 新建多列
dt[, `:=`(
  nc1 = 1:32,
  nc2 = paste(Hair, Eye, sep=',')
)]

# 基于条件新建列
dt[, nc3 := ifelse(Freq >= 10, 1, 0)]
dt[Freq >= 20, nc4 := 2]

# 基于函数新建列
ncols = c('nc', 'nc0') 
dt[, 
   (ncols) := lapply(.SD, function(x) x^0.5 + 1), 
   .SDcols = ncols]

# 删除列
dt[, nc := NULL]
dt[, (c('nc0','nc1','nc2','nc3','nc4')) := NULL]
# 新建一列
df = df.assign(nc = pd.Series(range(32)))
df.loc[:,'nc0'] = pd.Series(range(32), index=df.index)

# 新建多列
df = df.assign(
  nc1 = pd.Series(range(32)),
  nc2 = df.Hair + ',' + df.Eye
)

# 基于条件新建列
df = df.assign(nc3 = df.Freq.apply(lambda x: 1 if x >= 10 else 0))
df.loc[df.Freq >= 20, 'nc4'] = 2

# 基于函数新建列
ncols = ['nc', 'nc0']
df.loc[:, ncols] = df[ncols].apply(lambda x: x**0.5 + 1)

# 删除列
df = df.drop('nc', axis=1)
df.drop(['nc0','nc1','nc2','nc3','nc4'], axis=1, inplace=True)

columns summarise

# 对一列进行计算
dt[, unique(Eye)]
dt[, table(Eye)]

dt[, max(Freq)]

# 对多列进行计算
## 所有列的最大值
dt[, lapply(.SD, max)] 
## 所有列的缺失率
dt[, lapply(.SD, function(x) mean(is.na(x)))] 

## 对部分列计算缺失率,且可扩展到其他函数
sel_cols = c('Hair', 'Sex', 'Freq')
dt[, lapply(.SD, function(x) mean(is.na(x))), .SDcols = sel_cols]
# 基于列的计算
df.Eye.unique()
df.Eye.value_counts()

df.Freq.max()

# 对多列进行计算
## 所有列的最大值
df.max() 
## 所有列的缺失率 
df.isnull().mean() 

## 对部分列计算缺失率,且可扩展到其他函数
sel_cols = ['Hair', 'Sex', 'Freq']
df[sel_cols].apply(lambda x: x.isnull().mean())

分组汇总计算

grouped summarise

# max freq
dt[, .(freq_max = max(Freq)), by = 'Sex']

# count
dt[, .(freq_count = .N), keyby = c('Hair', 'Sex')]
# max freq # pandas的groupby会自动删除缺失变量
df.groupby('Sex').agg({'Freq':'max'}).
  rename(columns={'Freq':'freq_max'}).
  reset_index()

# count
df.groupby(['Hair','Sex']).agg({'Freq':'count'}).
  rename(columns={'Freq':'freq_count'}).
  reset_index()

grouped mutate & filter

# 基于sex分组,新建一列等于freq的最大值
dt[, freq_max := max(Freq), by = 'Sex']

# 基于sex分组,选取freq最大的行
dt[order(Freq)][, .SD[.N], by = 'Sex'][]
# 基于sex分组,新建一列等于freq的最大值
df.loc[:,'freq_max'] = df.groupby('Sex')['Freq'].transform(max)

# 基于sex分组,选取freq最大的行
df.sort_values('Freq').groupby('Sex').tail(1)

行列转换

长宽表转换

# 长表转宽表
dt_w = dcast(dt, Hair+Sex~Eye, value.var = 'Freq', fun.aggregate = sum)

# 宽表转长表
dt_l = melt(dt_w, id = c('Hair','Sex'), variable.name = 'Eye', value.name = 'Freq')
# 长表转宽表
df_w = pd.pivot_table(df, index=['Hair','Sex'], columns='Eye', values='Freq', aggfunc = sum).reset_index()

# 宽表转长表
df_l = df_w.melt(id_vars = ['Hair','Sex'], var_name='Freq')

行列合并切割

# 一行切割为多行
dtr = dt[, paste0(Eye, collapse = ','), keyby = c('Hair', 'Sex')]
dtr[, .(Eye = unlist(strsplit(V1, ','))), by = c('Hair', 'Sex')]

# 一列切割为多列
dtc = dt[, .(Hair, eye_sex = paste(Eye, Sex, sep = ','))]
dtc[, c('Eye', 'Sex') := tstrsplit(eye_sex, ',')]
# 一行切割为多行
dfr = df.groupby(['Hair','Sex'])['Eye'].apply(lambda x: ','.join(x)).reset_index()
dfr.assign(Eye = dfr['Eye'].str.split(',')).explode('Eye')

# 一列切割为多列
dfc = df[['Hair']].assign(eye_sex = df.Eye+','+df.Sex)
dfc['Eye'], dfc['Sex']= dfc['eye_sex'].str.split(',', 1).str

数据框合并

数据框行合并

# 合并两个数据框
dtr1 = rbind(dt[sample(.N,2)], dt[sample(.N,3)])
# 如果两个数据框的列名不一致,需要添加fill为TRUE
dtr2 = rbind(dt[sample(.N,2)], dt[sample(.N,3), .(Hair)], fill=TRUE)

# 直接合并多个数据框组成的 list
dt_lst = list(
  dt1 = dt[sample(.N,2)], 
  dt2 = dt[sample(.N,3)], 
  dt3 = dt[sample(.N,4)])
dt_bind = rbindlist(dt_lst, idcol = 'dt')
# 合并两个数据框
dfr1 = pd.concat([df.sample(n=2), df.sample(n=3)])
# 如果两个数据框的列名不一致
dfr2 = pd.concat([df.sample(n=2), df.sample(n=3)[['Hair']]], sort=False)

# 直接合并多个数据框组成的 list
df_lst = [
  df.sample(2), 
  df.sample(3), 
  df.sample(4)]
df_con = pd.concat(df_lst, axis=0)

数据框列合并

# merge
# pd.merge