记录Python.pandas库的常用方法,持续更新中。

建表

pd.DataFrame()

可以快速新建DataFrame,或将其他数据结构转换为DataFrame。

Python
# 新建df
df = pd.DataFrame(columns=example_columns)

# 二维列表转df
df_mat = pd.DataFrame(mat, columns=example_columns)

# 字典转df
example_dict = {column_0: data_list_0, column_1: data_list_1}
df_dict = pd.DataFrame(example_dict)

# 创建分层索引的df
'''
比如现在行、列索引均有两层,且分别预先存放在四个不同的列表中:
row_outer, row_inner, column_outer, column_inner
'''
# 创建分层行索引
row_tuples = [(o, i) for o in row_outer for i in row_inner]  # 注意先外后内
rows = pd.MultiIndex.from_tuples(row_tuples, name=['row_o', 'row_i'])
# 创建分层列索引
column_tuples = [(o, i) for o in column_outer for i in column_inner]
columns = pd.MultiIndex.from_tuples(column_tuples, name=['col_o', 'col_i'])
# 根据分层索引创建空表
df_multi = pd.DataFrame(index=rows, columns=columns)

pd.concat()

用于按照简单规则合并DataFrame。

Python
df_acum = pd.concat([df_a, df_b], ignore_index=True)  # 将df_b按行合并到df_a下方,并使索引保持连续
df_acum_join = pd.concat([df_a, df_b], ignore_index=True, join='inner')  # 可选连接方式为SQL风格(左、右、内、外),默认为外连接,这里内连接即为过滤掉二者中所有不匹配的列
df_acum_by_column = pd.concat([df_a, df_b], axis=1)  # 将df_b按列合并到df_a右侧

pd.merge()

用于在不同DataFrame中依据相同column(s)连接数据。

Python
# 将df_a的部分数据连接至df_b
df_merged = pd.merge(df_b, df_a, on=same_columns, how='left')  # 可选连接方式为SQL风格,同上

清洗

df.select_dtypes()

选取DataFrame中为/不为特定类型的列。

Python
# 选取内容不为字符串的列
df_str_exclude = df.select_dtypes(exclude='object')

# 选取内容为整型和浮点型的列
df_num_include = df.select_dtypes(include=['int64', 'float64'])

df.sort_values()

根据columns对行排序。

Python
return df.sort_values(by=['example'])

# 如果不同column的排序法则不一样
df_sorted = df.sort_values(by=['example_a', 'example_b'], ascending=[True, False])

df.drop()

去除特定的column(s)。

Python
print(df.drop(columns=example_columns))

# 不推荐用原DataFrame接收drop后的DataFrame,事实上如果这样做了,pandas也会报warning
df_dropped = df.drop(columns=example_columns)

df.drop_duplicates()

对特定的column(s)去重。

Python
df = df.drop_duplicates(subset=example_columns, keep='first')

df.dropna() · df.fillna()

前者是去掉含Null值的数据行的常用方法;后者是填充Null值的常用方法,除了直接设置替换后的值,也可以使用统计量值等。

Python
# dropna() method
df_dropna = df.dropna()  # 数据行只要有一个空值就删除
df_dropna_all = df.dropna(how='all')  # 数据行全为空值才删除
df_dropna_by_column = df.dropna(axis=1)  # 对列操作

# fillna() method
df[example_columns].fillna(0, inplace=True)

# 填充为统计量值
mean_value = df['example'].mean()  # 均值
# median_value = df['example'].median()  # 中位数
# mode_value = df['example'].mode()  # 众数
df['example'].fillna(mean_value, inplace=True)

# 用前、后数据进行填充
df['example_2'].fillna(method='pad')  # 用前一个数据填充
df['example_3'].fillna(mathod='bfill')  # 用后一个数据填充

df.interpolate()

Null值的插值填充方法。这一方法支持更加复杂的填充思路。

嗯~ pandas的interpolate()方法真的很有用,它可以帮助填充数据中的缺失值。这个方法支持的method参数主要有:

  • linear: 默认选项,它会对缺失值进行线性插值。
  • time: 如果索引是时间类型,这个选项会基于时间间隔进行线性插值。
  • indexvalues: 分别基于索引的数值或其实际值进行线性插值。
  • padffill: 用前一个非缺失值来填充该缺失值。
  • bfillbackfill: 用下一个非缺失值来填充该缺失值。
  • nearest: 用最近的非缺失值来填充缺失值。
  • zeroslinearquadraticcubicsplinebarycentricpolynomial: 这些是基于不同类型的插值方法,比如零阶、线性、二次、三次、样条以及多项式插值等。
  • kroghpiecewise_polynomialsplinepchipakima: 它们也提供了不同的插值算法,各自有着独特的计算方式和应用场景。

Python
df['example'].interpolate()  # 不设置任何参数,就是默认的'linear'线性插值方法
df['example_2'].interpolate(method='pad', limit=1)  # 使用'pad'方法,且控制只填充邻近非null值的那个

切片

与 · 或 · 非

Python
# 非运算
df = df[~df['example'].isin(['par_value'])]

# 与、或运算
df = df[((df['age'] < 60) & (df['age'] > 49)) | df['mode'] == 'extra']  # 其实这里的==也推荐转换成isin()的形式,用前者匹配容易出现意想不到的错误

索引方法

常用索引方法包含df.locdf.ilocdf.atdf.iatdf.xs等。当然,索引方法除了用来访问值外,也可以用来赋值。

Python
# df.loc method 基于标签索引切片
print(df.loc[0])  # 选择标签为0的行,值得注意的是,由于没有自增自然数索引时DataFrame也会自创一个,因此这种索引标签是必然存在的,且效果和下述df.iloc相同
print(df.loc[:, 'example_column'])  # 选择'example_column'列
print(df.loc[0: 1, example_columns])  # 选择多行多列
df.loc[0] = value_list  # 重新设置标签为0的行的值,以下其他方法不赘述
df.loc[('row_outer', 'row_inner'), ('col_outer', 'col_inner')] = 'edited_value'  # 修改一个多层DataFrame的某行某列值,以下其他方法不赘述
df.loc[df['example_column'].isin(['example_value']), 'example_column'] = 'edited_value'  # 修改某列某类值,以下其他方法不赘述
df.loc[len(df)] = new_value_list  # 在最下添加一行数据,很有用的方法

# df.iloc method 基于位置索引切片
print(df.iloc[0])  # 选择第0行
print(df.iloc[:, 0])  # 选择第0列
print(df.iloc[0: 2, 0: 2])  # 选择多行多列,这里和df.loc思路是不同的,df.loc基于标签,因此为闭区间;df.iloc基于位置,因此为左闭右开区间

# df.at 基于标签索引进行单个元素访问,此条件下效率高于df.loc
print(df.at[0, 'example_column'])

# df.iat 基于位置索引进行单个元素访问,此条件下效率高于df.iloc
print(df.iat[0, 1])

# df.xs 基于标签索引访问多层DataFrame特定级别的数据
'''
params
key: 标签或标签元组
axis: 指定轴
level: 指定层数,如双层表0为外层,1为内层
'''
print(df.xs(key, axis=0, level=0))  # 选择最外层的index为key的数据
print(df.xs(key_tuple, axis=1, level=1))  # 选择层数为1的column属于key_tuple元组的数据
print(df[outer_key].xs(inner_key, level=1, axis=0))  # 在最外层column为outer_key的条件下,选择层数为1的index为inner_key的数据
print(df.xs(key, axis=1, level=1) < 0.05)  # df.xs的判断语句写法

分组

df.groupby()

用于分组聚合。

Python
# 分组统计频数
df_by_example = df.groupby('example_a')
return df_by_example['example_b'].count()

# 聚合后,如果不想破坏原DataFrame的结构:reset_index()
df_count = df.groupby(['example_a', 'example_b']).sum().reset_index()

# 选出前10个频数最大的组,导出名称
grouped_example = df.groupby('example').size()
top_10_example = grouped_example.nlargest(10)
return top_10_example.index.tolist()

'''
所以groupby()到底让DataFrame产生了什么变化?
df.groupby()返回了一个pandas.core.groupby.DataFrameGroupBy对象,这个对象是一个特殊的“分组”对象,是一个中间状态,保存了如何将原来的DataFrame按照指定的列或条件进行分组,因此后续可以对这个分组对象进行一些操作,比如聚合、过滤、变换等。
'''

# 根据example_a和example_b进行分组,返回每组example_c值最大的3个/5个样本的该值合计
def top_n_sum(group, n):
    return group['example_c'].head(n).sum()
df_sorted = df.sort_values(by=['example_a', 'example_b', 'example_c'], ascending=[True, True, False])
result = []
for (a, b), group in df_sorted.groupby(['example_a', 'example_b']):  # 注意这里的思想
    top3_sum = top_n_sum(group, 3)
    top5_sum = top_n_sum(group, 5)
    result.append({'example_a': a, 'example_b': b, 'top3_sum': top3_sum, 'top5_sum': top5_sum})
result_df = pd.DataFrame(result)

pd.cut()

用于将column的值分箱。

Python
# 比如对年龄进行分箱
df['age_cut'] = pd.cut(df['age'], bins=[0, 18, 25, 39, 49, 59, 100])

pd.qcut()

基于样本的分位数进行分箱,兴许在可视化的时候用得上。

Python
# 将size按照其数值大小分布分成6个等长的箱
df['size_cut'] = pd.qcut(df['size'], 6)

# 或者按照自定义的分位数进行分箱
df['size_cut_customized'] = pd.qcut(df['size'], [0, 0.25, 0.75, 1.0])

描述分析

常用统计量

均含有默认参数axis=0,其为1时表示对行操作。

Python
return df['example'].sum()  # 总和
return df['example'].mean()  # 均值
return df['example'].median()  # 中位数
return df['example'].mode()  # 众数
return df['example'].cumsum()  # 累积,这个不是聚合值

df.value_counts()

用于统计某个column中不同值的频数。

Python
# 输出各不同值的频数
return df['example'].value_counts()

# 如果要输出百分比,可以在此基础上采用链式编程
return df['example'].value_counts(nomalize=True).mul(100).round(2).astype(str) + '%'

df.count() · df.nunique()

用于统计某个column中有多少个值/不同值。均不会将空值纳入统计。

Python
return df['example'].count()
return df['example'].nunique()

df.iterrows()

pandas提供的迭代方法,可以用于写循环结构等。不过需要遍历时还是更推荐用下面的批量操作方法。

Python
for index, row in df.iterrows():
  print(index, row['example'])

df.apply()

pandas提供的批量操作方法,可以用于实现一些复杂功能。pandas里面的大部分数据清洗方法是没必要详记的,apply()可以揽下所有逐行/逐列数据清洗的活,且集成性更强,可能还有效率上的提升。

Python
mapping = {1: 'a', 2: 'b', 3: 'c'}

def value_map(val):
  # 把数字映射为字母
  return mapping.get(val, None)

# 单个column
df['example'] = df['example'].apply(value_map)

# 多个columns
df[example_columns] = df[example_columns].applymap(value_map)

# axis=1即按照1轴(即横轴)操作,比如按行求和
df['sum'] = df[example_columns].apply(np.sum, axis=1)

# 直接返回DataFrame也是可以的
# 额外编写的process_by_row()应该返回一个pd.Series结构,这样df_new就能逐行接收返回结果
from utils import process_by_row
df_new = df.apply(process_by_row, axis=1)
df_new.columns = [example_columns]

# 以及,apply()可以使用集成了tqdm的方法
from tqdm.auto import tqdm
tqdm.pandas(desc='Progress')
df_new_new = df.progress_apply(process_by_row, axis=1)

pd.crosstab()

pandas的交叉分析工具。

Python
cross_tab = pd.crosstab(df['example_A'], df['example_B'])

# 导出结果
print(cross_tab)
cross_tab.to_excel('crosstab_results.xlsx')

pd.pivot_table()

pandas的数据透视表工具,比Excel的聚合方式更丰富。重要参数包括values, index, columnsaggfunc等。聚合方法掌握这个已经足够。

参考文献:https://zhuanlan.zhihu.com/p/267208129

Python
'''
现在有一个展示销售情况的df,其columns为['id', 'city', 'state', 'product_category', 'sales', 'quantity', 'profit']
'''

# 获取每个州的销售总额和利润总额
result_1 = pd.pivot_table(df, index=['state'], values=['sales', 'profit'], aggfunc=np.sum)  

# 获取每个州每个城市每单平均销量
result_2 = pd.pivot_table(df, index=['state', 'city'], values='quantity', aggfunc=np.mean)  # 单个column不转成列表直接传入也行

# 获取每个州的总销量和每单平均销量
result_3 = pd.pivot_table(df, index='state', values='quantity', aggfunc=[np.sum, np.mean])  # 多个aggfunc同样要转成列表传入

# 获取每个城市(行)每类商品(列)的总销售量,并添加行列总计
result_4 = pd.pivot_table(df, index='city', values='quantity', columns='product_category', aggfunc=np.sum)  # 注意这里的columns参数实际表达的是列分组键(GROUP BY)的含义,和values是不同的

def calc_profit_rate(series):
    return series.sum() / sum_profit
# 获取每个州的利润总额占比
sum_profit = df['profit'].sum()
result_5 = pd.pivot_table(df, index='state', values='profit', aggfunc=calc_profit_rate)  # aggfunc参数也接受自定义方法