SOTON私人定制:利用Python进行数据分析(分组运算)

简介: 数据分析比较常见的步骤是将对数据集进行分组然后应用函数,这步也可以称之为分组运算。Hadley Wickham大神为此创造了一个专用术语“split-apply-combine",即拆分-应用-合并。

数据分析比较常见的步骤是将对数据集进行分组然后应用函数,这步也可以称之为分组运算。Hadley Wickham大神为此创造了一个专用术语“split-apply-combine",即拆分-应用-合并。那么当我们谈论分组运算的时候,我们其实在谈论什么呢?

  • Splitting:根据标准对数据进行拆分分组
  • Applying: 对每组都分别应用一个函数
  • Combining: 将结果合并新的数据结构

分组运算一般要求的数据存放格式为“长格式”,所以先介绍“长格式”和“宽格式”的转换,然后是分组运算的具体操作。

“长格式“ VS”宽格式“

数据的常见保存方式有两种,长格式(long format)或是宽格式(wide format)

  • wide format
基因 分生组织
gene1 582 91 495
gene2 305 3505 33

在宽格式下,类别型变量单独成列。如上的植物的不同部位分为3列,列中的数据表示为表达量。看起来就非常的直观,而且日常生活中也是按照如此方法记录数据。

  • long format
基因 组织 表达量
gene1 分生组织 582
gene2 分生组织 305
gene1 91
gene2 3503
gene1 492
gene2 33

而长结构的数据则是类别型变量定义为专门的一列。通常情况下,关系型数据库(如MySQL)通常以长格式存储数据,这是因为固定架构下随着表中数据的增加或删除, 类别行(如组织)能够增加或减少。

后续的分组操作其实更倾向于数据是以长格式进行保存,所以这里先介绍pandas是如何进行长格式和宽格式之间的转换的。

首先创建一个用于测试的长格式数据,方法如下:

import pandas.util.testing as tm; tm.N = 3
def unpivot(frame):
    N, K = frame.shape
    data = {'value' : frame.values.ravel('F'),
            'variable' : np.asarray(frame.columns).repeat(N),
            'date' : np.tile(np.asarray(frame.index), K)}
    return pd.DataFrame(data, columns=['date', 'variable', 'value'])
ldata = unpivot(tm.makeTimeDataFrame())

然后把长格式变成宽格式,可认为是把数据的列“旋转” 为行

# 第一种方法: pivot
# pd.pivot(index, columns, values), 对应索引,类别列和数值列
pivoted = ldata.pivot('date','variable','value')
# 第二种方法: unstack
## 先用set_index建立层次化索引
unstacked = ldata.set_index(['date','varibale']).unstack('variable')

再把宽格式变为长格式,也可以认为是把数据的行“旋转”成列。

# 先把unstacked数据还原成普通的DataFrame
wdata = nstacked.reset_index()
wdata.columns = ['date','A','B','C','D']
# 第一种方法:melt
pd.melt(wdata, id_vars=['date'])
# 第二种方法: stack
# 如果原始数据没有索引,需要用set_index重建
wdata.set_index('date').stack()

GroupBy

第一步是将数据集根据一定标准,即分组键,拆分多个组,pandas提供了grouby方法,能够根据如下形式的分组键工作:

  • 列表和数组(Numpy array),其长度与待分组的轴一致
  • DataFrame的某个列名的值
  • 字典或Series,提供了待分组轴上的值与分组名(label -> group name)之间的对应关系
  • 根据多层次的轴索引
  • Python函数,用于轴索引或索引中的各个标签

下面举例说明:
形式一:根据某一列的值,一般是类别型数值

df = DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
                          'foo', 'bar', 'foo', 'foo'],
                  'B' : ['one', 'one', 'two', 'three',
                          'two', 'two', 'one', 'three'],
                   'C' : np.random.randn(8),
                   'D' : np.random.randn(8)})
# 根据A列进行拆分
df.groupby('A')
# 根据A和B列进行拆分
df.groupby(['A','B'])

这会产生一个pandas.core.groupby.DataFrameGroupBy object对象,没有进行实质性的运算操作,只产生了中间信息。你可以查看分组之后每一组的大小

df.groupby(['A','B']).size()

形式二: 根据函数. 下面定义了一个函数根据列名是否属于元音进行拆分

def get_letter_type(letter):
    if letter.lower() in 'aeiou':
        return 'vowel'
    else:
        return 'consonant'

grouped = df.groupby(get_letter_type, axis=1).

形式三: 根据层次索引。 不同于R语言data.frame只有一个行或列名,pandas的DataFrame允许多个层次结构的行或列名。

# 构建多层次索引的数据
arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'],
           ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']]
index = pd.MultiIndex.from_arrays(arrays, names=['first', 'second'])
s = pd.Series(np.random.randn(8), index=index)
# 根据不同层次的索引进行分组
level1 = s.groupby(level='first') # 第一层
level2 = s.groupby(level=1) # 第二层

形式四: 根据字典或Series进行分组
可以通过字典定义分组信息的映射。

mapping = {'A':'str','B':'str','C':'values','D':'values','E':'values'}
df.groupby(mapping, axis=1)

形式四: 同时根据行索引和列数值进行分组

df.index = index
df.groupby([pd.Grouper(level='first'),'A']).mean()

分组对象迭代: 上面产生的GroupBy分组对象都是可以进行迭代,会产生一个一组二元元组。

# 对于单键值
for name, group in df.groupby(['A']):
    print(name)
    print(group)
# 对于多个键值
for (k1,k2), group in df.groupby(['A']):
    print(k1,k2)
    print(group)

选择分组:我们可以在分组结束后用get_group()提取某个特定的组

df.groupby('A').get_group('foo')
# 下面是结果
                A      B         C         D         E
first second                                          
bar   one     foo    one  0.709603 -1.023657 -0.321730
baz   one     foo    two  1.495328 -0.279928 -0.297983
foo   one     foo    two  0.495288 -0.563845 -1.774453
qux   one     foo    one  1.649135 -0.274208  1.194536
      two     foo  three -2.194127  3.440418 -0.023144
# 多个组
df.groupby(['A', 'B']).get_group(('bar', 'one'))

Apply

分组步骤较为直接,而应用函数步骤则变化较多。这一步,我们可能会做如下操作:

  • 数据聚合(Aggregation):分别计算各组的统计信息,如均值,大小等
  • 转换(Transformation): 对每一组进行特异性的计算,如标准化,缺失值插值等
  • 过滤(Filtration): 根据统计信息进行筛选,舍弃部分组。

数据聚合:Aggregation

所谓的聚合,也就是数组产生标量值的数据转换过程。我们可以使用mean, count, min, sum等一般性方法,也可以用GroupBy对象创建后的aggregate或等价的agg方法。

grouped = df.groupby('A')
grouped.mean()
# 等价于
grouped.agg(np.mean)

对于多个分组而言,会产生层次索引的结果,如果希望层次索引成为单独一列的话,需要在groupby用到as_index选项。或者最后使用reset_index方法

# as_index
grouped = df.groupby(['A','B'], as_index=False)
grouped.agg(np.mean)
# reset_index
df.groupby(['A','B']).sum().reset_index()

aggregate或等价的agg还允许对每一列使用多个统计函数

df.groupby('A').agg([np.sum,np.mean,np.std])
# 语法糖 df.groupby('A').C 等价于
# grouped=df.groupby(['A'])
# grouped['C']
df.groupby('A').C.agg([np.sum,np.mean,np.std])

或者是不同列使用不同函数,比如说对D列计算标准差,对列求平均

grouped=df.groupby(['A'])
grouped.agg({'D': np.std , 'C': np.mean})

注:目前sum, mean, std和sem方法对Cython进行了优化。

转换: Transformation

transform返回的对象和原来分组数据具有相同的大小。转换所用的函数必须满足如下条件:

  • 返回的结果大小要与原先的组块(group chunk)一致
  • 在组块中逐列操作
  • 并非在组块上原位运算。原先的组块被认为是不可修改的,任何对原来组块的修改可能会导致意想不到的结果。如果你用到了fillna,必须是grouped.transform(lambda x: x.fillna(inplace=False))

举例说明,比如说我们相对每一组数据进行标准化。当然我们先得有一组数据, 随机生成一组从1999年到2002年,然后以100天为一个滑窗(window),计算每一个滑窗的均值(rolling)。

# date_range产生日期索引
index = pd.date_range('10/1/1999', periods=1100)
ts = pd.Series(np.random.normal(0.5, 2, 1100), index)
ts = ts.rolling(window=100,min_periods=100).mean().dropna()

然后要根据年份进行分组,对各组中的数据进行标准化,计算zscore.

# 两个匿名函数,用于分组和计算zscore
key = lambda x : x.year
zscore = lambda x : (x - x.mean())/x.std()
transformed = ts.groupby(key).transform(zscore)

应用zscore的过程中使用了广播(broadcast)技术。让我们可视化一下转换前后数据形状

compare = pd.DataFrame({'Original': ts, 'Transformed': transformed})
compare.plot()
img_e4a0c514b0a7e08fdccb33dfbb192832.png
转换前后

过滤:Filtration

过滤返回原先数据的子集。比如说上面时间周期数据,过滤掉均值小于0.55的年份。

ts.groupby(key).filter(lambda x : x.mean() > 0.55)

Apply: 更加灵活的“拆分-应用-合并”

上述的aggregatetransform都有一定的局限性,传入的函数只能有两个结果,要么是产生一个可以广播的标量值,要么是产生相同大小的数组。apply是一个更加一般化的函数,能完成上面所描述的所有任务,还能做得更好。

  • 分组返回描述性统计结果
df.groupby('A').apply(lambda x : x.describe())
# 效果等价于df.groupby('A').describe()
img_8c7662283c422fa3a880bb2bb6c5e62f.png
apply
  • 修改返回的分组名
def f(group):
     return pd.DataFrame({'original' : group,
                          'demeaned' : group - group.mean()})
df.groupby('A')['C'].apply(f)
              demeaned  original
first second                    
bar   one     0.278558  0.709603
      two     0.280707 -0.140075
baz   one     1.064283  1.495328
      two    -0.848109 -1.268891
foo   one     0.064243  0.495288
      two     0.567402  0.146620
qux   one     1.218089  1.649135
      two    -2.625172 -2.194127

更多有用的技巧如获取每一组的第n行,见官方文档

目录
相关文章
|
16天前
|
数据采集 存储 数据挖掘
Python数据分析:Pandas库的高效数据处理技巧
【10月更文挑战第27天】在数据分析领域,Python的Pandas库因其强大的数据处理能力而备受青睐。本文介绍了Pandas在数据导入、清洗、转换、聚合、时间序列分析和数据合并等方面的高效技巧,帮助数据分析师快速处理复杂数据集,提高工作效率。
45 0
|
11天前
|
机器学习/深度学习 数据采集 数据挖掘
解锁 Python 数据分析新境界:Pandas 与 NumPy 高级技巧深度剖析
Pandas 和 NumPy 是 Python 中不可或缺的数据处理和分析工具。本文通过实际案例深入剖析了 Pandas 的数据清洗、NumPy 的数组运算、结合两者进行数据分析和特征工程,以及 Pandas 的时间序列处理功能。这些高级技巧能够帮助我们更高效、准确地处理和分析数据,为决策提供支持。
26 2
|
17天前
|
存储 数据挖掘 数据处理
Python数据分析:Pandas库的高效数据处理技巧
【10月更文挑战第26天】Python 是数据分析领域的热门语言,Pandas 库以其高效的数据处理功能成为数据科学家的利器。本文介绍 Pandas 在数据读取、筛选、分组、转换和合并等方面的高效技巧,并通过示例代码展示其实际应用。
30 2
|
8天前
|
并行计算 数据挖掘 大数据
Python数据分析实战:利用Pandas处理大数据集
Python数据分析实战:利用Pandas处理大数据集
|
8天前
|
数据采集 数据可视化 数据挖掘
利用Python进行数据分析:Pandas库实战指南
利用Python进行数据分析:Pandas库实战指南
|
10天前
|
SQL 数据挖掘 Python
数据分析编程:SQL,Python or SPL?
数据分析编程用什么,SQL、python or SPL?话不多说,直接上代码,对比明显,明眼人一看就明了:本案例涵盖五个数据分析任务:1) 计算用户会话次数;2) 球员连续得分分析;3) 连续三天活跃用户数统计;4) 新用户次日留存率计算;5) 股价涨跌幅分析。每个任务基于相应数据表进行处理和计算。
|
11天前
|
数据采集 数据可视化 数据挖掘
使用Python进行数据分析和可视化
【10月更文挑战第33天】本文将介绍如何使用Python编程语言进行数据分析和可视化。我们将从数据清洗开始,然后进行数据探索性分析,最后使用matplotlib和seaborn库进行数据可视化。通过阅读本文,你将学会如何运用Python进行数据处理和可视化展示。
|
22天前
|
数据采集 机器学习/深度学习 数据可视化
深入浅出:用Python进行数据分析的入门指南
【10月更文挑战第21天】 在信息爆炸的时代,掌握数据分析技能就像拥有一把钥匙,能够解锁隐藏在庞大数据集背后的秘密。本文将引导你通过Python语言,学习如何从零开始进行数据分析。我们将一起探索数据的收集、处理、分析和可视化等步骤,并最终学会如何利用数据讲故事。无论你是编程新手还是希望提升数据分析能力的专业人士,这篇文章都将为你提供一条清晰的学习路径。
|
3月前
|
数据采集 数据可视化 数据挖掘
数据分析大神养成记:Python+Pandas+Matplotlib助你飞跃!
在数字化时代,数据分析至关重要,而Python凭借其强大的数据处理能力和丰富的库支持,已成为该领域的首选工具。Python作为基石,提供简洁语法和全面功能,适用于从数据预处理到高级分析的各种任务。Pandas库则像是神兵利器,其DataFrame结构让表格型数据的处理变得简单高效,支持数据的增删改查及复杂变换。配合Matplotlib这一数据可视化的魔法棒,能以直观图表展现数据分析结果。掌握这三大神器,你也能成为数据分析领域的高手!
79 2
|
3月前
|
机器学习/深度学习 数据采集 数据可视化
基于爬虫和机器学习的招聘数据分析与可视化系统,python django框架,前端bootstrap,机器学习有八种带有可视化大屏和后台
本文介绍了一个基于Python Django框架和Bootstrap前端技术,集成了机器学习算法和数据可视化的招聘数据分析与可视化系统,该系统通过爬虫技术获取职位信息,并使用多种机器学习模型进行薪资预测、职位匹配和趋势分析,提供了一个直观的可视化大屏和后台管理系统,以优化招聘策略并提升决策质量。
178 4