目录
什么是Pandas?
Pandas常用操作
pandas之Series创建
pandas之Series的索引和值
pandas之读取外部数据
pandas之DataFrame
pandas之取行或者列
pandas之loc
pandas之iloc
pandas之布尔索引
pandas之字符串方法
缺失数据的处理
pandas常用统计方法
数据合并之join
数据合并之merge
分组和聚合
索引和复合索引
Series复合索引
DataFrame复合索引
时间序列
生成一段时间范围
关于频率的更多缩写
在DataFrame中使用时间序列
pandas重采样
PeriodIndex
Pandas的数据结构
Series
DataFrame
Pandas的索引操作
索引对象Index
Series索引
DataFrame索引
高级索引:标签、位置和混合
Pandas的对齐运算
Series的对齐运算
DataFrame的对齐运算
填充未对齐的数据进行运算
Pandas的函数应用
apply 和 applymap
排序
处理缺失数据
层级索引(hierarchical indexing)
MultiIndex索引对象
选取子集
交换分层顺序
交换并排序分层
Pandas统计计算和描述
常用的统计计算
常用的统计描述
常用的统计描述方法:
Pandas分组与聚合
分组 (groupby)
一、GroupBy对象:DataFrameGroupBy,SeriesGroupBy
二、GroupBy对象支持迭代操作
聚合 (aggregation)
数据的分组运算
groupby.apply(func)
数据清洗
数据连接(pd.merge)
数据合并(pd.concat)
数据重构
数据转换
一、 处理重复数据
二、数据替换
聚类模型:K-Means
K-Means算法
算法思想:
算法描述:
优缺点:
全球食品数据分析
什么是Pandas?
Pandas的名称来自于面板数据(panel data)和Python数据分析(data analysis)。
Pandas是一个强大的分析结构化数据的工具集,基于NumPy构建,提供了 高级数据结构 和 数据操作工具,它是使Python成为强大而高效的数据分析环境的重要因素之一。
-
一个强大的分析和操作大型结构化数据集所需的工具集
-
基础是NumPy,提供了高性能矩阵的运算
-
提供了大量能够快速便捷地处理数据的函数和方法
-
应用于数据挖掘,数据分析
-
提供数据清洗功能
http://pandas.
Pandas常用操作
pandas之Series创建
pandas之Series创建
pandas之Series的索引和值
pandas之读取外部数据
数据存在csv中,直接使用pd. read_csv即可
对于数据库比如mysql或者mongodb中数据:pd.read_sql(sql_sentence,connection)
pandas之DataFrame
DataFrame对象既有行索引,又有列索引
行索引,表明不同行,横向索引,叫index,0轴,axis=0
列索引,表名不同列,纵向索引,叫columns,1轴,axis=1
df.sort_values(by="Count_AnimalName",ascending=False) #排序
pandas之取行或者列
df_sorted = df.sort_values(by="Count_AnimalName")
df_sorted[:100]
那么问题来了:
我们具体要选择某一列该怎么选择呢?df[" Count_AnimalName "]
我们要同时选择行和列改怎么办?df[:100][" Count_AnimalName "]
pandas之loc
还有更多的经过pandas优化过的选择方式:
1.df.loc 通过标签索引行数据
2.df.iloc 通过位置获取行数据
pandas之iloc
赋值更改数据的过程:
pandas之布尔索引
pandas之字符串方法
缺失数据的处理
我们的数据缺失通常有两种情况:
一种就是空,None等,在pandas是NaN(和np.nan一样)
另一种是我们让其为0,蓝色框中
对于NaN的数据,在numpy中我们是如何处理的?
在pandas中我们处理起来非常容易
判断数据是否为NaN:pd.isnull(df),pd.notnull(df)
处理方式1:删除NaN所在的行列dropna (axis=0, how='any', inplace=False)
处理方式2:填充数据,t.fillna(t.mean()),t.fiallna(t.median()),t.fillna(0)
处理为0的数据:t[t==0]=np.nan
当然并不是每次为0的数据都需要处理
计算平均值等情况,nan是不参与计算的,但是0会
pandas常用统计方法
数据合并之join
join:默认情况下他是把行索引相同的数据合并到一起
数据合并之merge
默认的合并方式inner,并集
merge outer,交集,NaN补全
merge left,左边为准,NaN补全
merge right,右边为准,NaN补全
分组和聚合
在pandas中类似的分组的操作我们有很简单的方式来完成
df.groupby(by="columns_name")
那么问题来了,调用groupby方法之后返回的是什么内容?
grouped = df.groupby(by="columns_name")
grouped是一个DataFrameGroupBy对象,是可迭代的
grouped中的每一个元素是一个元组
元组里面是(索引(分组的值),分组之后的DataFrame)
那么,回到之前的问题:
要统计美国和中国的星巴克的数量,我们应该怎么做?
分组之后的每个DataFrame的长度?
长度是一个思路,但是我们有更多的方法(聚合方法)来解决这个问题
DataFrameGroupBy对象有很多经过优化的方法
如果我们需要对国家和省份进行分组统计,应该怎么操作呢?
grouped = df.groupby(by=[df["Country"],df["State/Province"]])
很多时候我们只希望对获取分组之后的某一部分数据,或者说我们只希望对某几列数据进行分组,这个时候我们应该怎么办呢?
获取分组之后的某一部分数据:
df.groupby(by=["Country","State/Province"])["Country"].count()
对某几列数据进行分组:
df["Country"].groupby(by=[df["Country"],df["State/Province"]]).count()
观察结果,由于只选择了一列数据,所以结果是一个Series类型
如果我想返回一个DataFrame类型呢?
t1 = df[["Country"]].groupby(by=[df["Country"],df["State/Province"]]).count()
t2 = df.groupby(by=["Country","State/Province"])[["Country"]].count()
以上的两条命令结果一样
和之前的结果的区别在于当前返回的是一个DataFrame类型
那么问题来了:
和之前使用一个分组条件相比,当前的返回结果的前两列是什么?
索引和复合索引
简单的索引操作:
·获取index:df.index
·指定index :df.index = ['x','y']
·重新设置index : df.reindex(list("abcedf"))
·指定某一列作为index :df.set_index("Country",drop=False)
·返回index的唯一值:df.set_index("Country").index.unique()
假设a为一个DataFrame,那么当a.set_index(["c","d"])即设置两个索引的时候是什么样子的结果呢?
a = pd.DataFrame({'a': range(7),'b': range(7, 0, -1),'c': ['one','one','one','two','two','two', 'two'],'d': list("hjklmno")})
Series复合索引
DataFrame复合索引
时间序列
生成一段时间范围
pd.date_range(start=None, end=None, periods=None, freq='D')
start和end以及freq配合能够生成start和end范围内以频率freq的一组时间索引
start和periods以及freq配合能够生成从start开始的频率为freq的periods个时间索引
关于频率的更多缩写
在DataFrame中使用时间序列
index=pd.date_range("20170101",periods=10)
df = pd.DataFrame(np.random.rand(10),index=index)
回到最开始的911数据的案例中,我们可以使用pandas提供的方法把时间字符串转化为时间序列
df["timeStamp"] = pd.to_datetime(df["timeStamp"],format="")
format参数大部分情况下可以不用写,但是对于pandas无法格式化的时间字符串,我们可以使用该参数,比如包含中文
那么问题来了:
我们现在要统计每个月或者每个季度的次数怎么办呢?
pandas重采样
重采样:指的是将时间序列从一个频率转化为另一个频率进行处理的过程,将高频率数据转化为低频率数据为降采样,低频率转化为高频率为升采样
pandas提供了一个resample的方法来帮助我们实现频率转化
PeriodIndex
之前所学习的DatetimeIndex可以理解为时间戳
那么现在我们要学习的PeriodIndex可以理解为时间段
periods = pd.PeriodIndex(year=data["year"],month=data["month"],day=data["day"],hour=data["hour"],freq="H")
那么如果给这个时间段降采样呢? data = df.set_index(periods).resample("10D").mean()
Pandas的数据结构
import pandas as pd
Pandas有两个最主要也是最重要的数据结构: Series 和 DataFrame
Series
Series是一种类似于一维数组的 对象,由一组数据(各种NumPy数据类型)以及一组与之对应的索引(数据标签)组成。
- 类似一维数组的对象
- 由数据和索引组成
- 索引(index)在左,数据(values)在右
- 索引是自动创建的
1. 通过list构建Series
ser_obj = pd.Series(range(10))
示例代码:
ser_obj = pd.Series(range(10, 20))
运行结果:
<class 'pandas.core.series.Series'>
2. 获取数据和索引
ser_obj.index 和 ser_obj.values
示例代码:
运行结果:
[10 11 12 13 14 15 16 17 18 19] RangeIndex(start=0, stop=10, step=1)
3. 通过索引获取数据
ser_obj[idx]
示例代码:
运行结果:
4. 索引与数据的对应关系不被运算结果影响
示例代码:
运行结果:
5. 通过dict构建Series
示例代码:
year_data = {2001: 17.8, 2002: 20.1, 2003: 16.5} ser_obj2 = pd.Series(year_data)
运行结果:
Int64Index([2001, 2002, 2003], dtype='int64')
name属性
对象名:ser_obj.name
对象索引名:ser_obj.index.name
示例代码:
ser_obj2.index.name = 'year'
运行结果:
Name: temp, dtype: float64
DataFrame
DataFrame是一个表格型的数据结构,它含有一组有序的列,每列可以是不同类型的值。DataFrame既有行索引也有列索引,它可以被看做是由Series组成的字典(共用同一个索引),数据是以二维结构存放的。
- 类似多维数组/表格数据 (如,excel, R中的data.frame)
- 每列数据可以是不同的类型
- 索引包括列索引和行索引
1. 通过ndarray构建DataFrame
示例代码:
array = np.random.randn(5,4) df_obj = pd.DataFrame(array)
运行结果:
[[ 0.83500594 -1.49290138 -0.53120106 -0.11313932] [ 0.64629762 -0.36779941 0.08011084 0.60080495] [-1.23458522 0.33409674 -0.58778195 -0.73610573] [-1.47651414 0.99400187 0.21001995 -0.90515656] [ 0.56669419 1.38238348 -0.49099007 1.94484598]] 0 0.835006 -1.492901 -0.531201 -0.113139 1 0.646298 -0.367799 0.080111 0.600805 2 -1.234585 0.334097 -0.587782 -0.736106 3 -1.476514 0.994002 0.210020 -0.905157 4 0.566694 1.382383 -0.490990 1.944846
2. 通过dict构建DataFrame
示例代码:
'B': pd.Timestamp('20170426'), 'C': pd.Series(1, index=list(range(4)),dtype='float32'), 'D': np.array([3] * 4,dtype='int32'), 'E': ["Python","Java","C++","C"], df_obj2 = pd.DataFrame(dict_data)
运行结果:
0 1 2017-04-26 1.0 3 Python ITCast 1 1 2017-04-26 1.0 3 Java ITCast 2 1 2017-04-26 1.0 3 C++ ITCast 3 1 2017-04-26 1.0 3 C ITCast
3. 通过列索引获取列数据(Series类型)
df_obj[col_idx] 或 df_obj.col_idx
示例代码:
print(type(df_obj2['A']))
运行结果:
<class 'pandas.core.series.Series'>
4. 增加列数据
df_obj[new_col_idx] = data
类似Python的 dict添加key-value
示例代码:
df_obj2['G'] = df_obj2['D'] + 4
运行结果:
0 1.0 2017-01-02 1.0 3 Python ITCast 7 1 1.0 2017-01-02 1.0 3 Java ITCast 7 2 1.0 2017-01-02 1.0 3 C++ ITCast 7 3 1.0 2017-01-02 1.0 3 C ITCast 7
5. 删除列
del df_obj[col_idx]
示例代码:
运行结果:
0 1.0 2017-01-02 1.0 3 Python ITCast 1 1.0 2017-01-02 1.0 3 Java ITCast 2 1.0 2017-01-02 1.0 3 C++ ITCast 3 1.0 2017-01-02 1.0 3 C ITCast
Pandas的索引操作
索引对象Index
1. Series和DataFrame中的索引都是Index对象
示例代码:
print(type(ser_obj.index)) print(type(df_obj2.index))
运行结果:
<class 'pandas.indexes.range.RangeIndex'> <class 'pandas.indexes.numeric.Int64Index'> Int64Index([0, 1, 2, 3], dtype='int64')
2. 索引对象不可变,保证了数据的安全
示例代码:
运行结果:
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-23-7f40a356d7d1> in <module>() ----> 2 df_obj2.index[0] = 2 /Users/Power/anaconda/lib/python3.6/site-packages/pandas/indexes/base.py in __setitem__(self, key, value) 1403 def __setitem__(self, key, value): -> 1404 raise TypeError("Index does not support mutable operations") 1406 def __getitem__(self, key): TypeError: Index does not support mutable operations
常见的Index种类
- Index,索引
- Int64Index,整数索引
- MultiIndex,层级索引
- DatetimeIndex,时间戳类型
Series索引
1. index 指定行索引名
示例代码:
ser_obj = pd.Series(range(5), index = ['a', 'b', 'c', 'd', 'e'])
运行结果:
2. 行索引
ser_obj[‘label’], ser_obj[pos]
示例代码:
运行结果:
3. 切片索引
ser_obj[2:4], ser_obj[‘label1’: ’label3’]
注意,按索引名切片操作时,是包含终止索引的。
示例代码:
运行结果:
4. 不连续索引
ser_obj[[‘label1’, ’label2’, ‘label3’]]
示例代码:
print(ser_obj[[0, 2, 4]]) print(ser_obj[['a', 'e']])
运行结果:
5. 布尔索引
示例代码:
print(ser_obj[ser_obj > 2])
运行结果:
DataFrame索引
1. columns 指定列索引名
示例代码:
df_obj = pd.DataFrame(np.random.randn(5,4), columns = ['a', 'b', 'c', 'd'])
运行结果:
0 -0.241678 0.621589 0.843546 -0.383105 1 -0.526918 -0.485325 1.124420 -0.653144 2 -1.074163 0.939324 -0.309822 -0.209149 3 -0.716816 1.844654 -2.123637 -1.323484 4 0.368212 -0.910324 0.064703 0.486016
2. 列索引
df_obj[[‘label’]]
示例代码:
print(df_obj['a']) # 返回Series类型 print(df_obj[[0]]) # 返回DataFrame类型 print(type(df_obj[[0]])) # 返回DataFrame类型
运行结果:
<class 'pandas.core.frame.DataFrame'>
3. 不连续索引
df_obj[[‘label1’, ‘label2’]]
示例代码:
运行结果:
高级索引:标签、位置和混合
Pandas的高级索引有3种
1. loc 标签索引
DataFrame 不能直接切片,可以通过loc来做切片
loc是基于标签名的索引,也就是我们自定义的索引名
示例代码:
print(ser_obj.loc['b':'d']) print(df_obj.loc[0:2, 'a'])
运行结果:
2. iloc 位置索引
作用和loc一样,不过是基于索引编号来索引
示例代码:
print(df_obj.iloc[0:2, 0]) # 注意和df_obj.loc[0:2, 'a']的区别
运行结果:
3. ix 标签与位置混合索引
ix是以上二者的综合,既可以使用索引编号,又可以使用自定义索引,要视情况不同来使用,
如果索引既有数字又有英文,那么这种方式是不建议使用的,容易导致定位的混乱。
示例代码:
print(ser_obj.ix['b':'c']) print(df_obj.loc[0:2, 'a'])
运行结果:
注意
DataFrame索引操作,可将其看作ndarray的索引操作
标签的切片索引是包含末尾位置的
Pandas的对齐运算
是数据清洗的重要过程,可以按索引对齐进行运算,如果没对齐的位置则补NaN,最后也可以填充NaN
Series的对齐运算
1. Series 按行、索引对齐
示例代码:
s1 = pd.Series(range(10, 20), index = range(10)) s2 = pd.Series(range(20, 25), index = range(5))
运行结果:
2. Series的对齐运算
示例代码:
运行结果:
DataFrame的对齐运算
1. DataFrame按行、列索引对齐
示例代码:
df1 = pd.DataFrame(np.ones((2,2)), columns = ['a', 'b']) df2 = pd.DataFrame(np.ones((3,3)), columns = ['a', 'b', 'c'])
运行结果:
2. DataFrame的对齐运算
示例代码:
运行结果:
填充未对齐的数据进行运算
1. fill_value
使用add , sub , div , mul 的同时,
通过fill_value 指定填充值,未对齐的数据将和填充值做运算
示例代码:
s1.add(s2, fill_value = -1) df1.sub(df2, fill_value = 2.)
运行结果:
# s1.add(s2, fill_value = -1) # df1.sub(df2, fill_value = 2.)
Pandas的函数应用
apply 和 applymap
1. 可直接使用NumPy的函数
示例代码:
df = pd.DataFrame(np.random.randn(5,4) - 1)
运行结果:
0 -0.062413 0.844813 -1.853721 -1.980717 1 -0.539628 -1.975173 -0.856597 -2.612406 2 -1.277081 -1.088457 -0.152189 0.530325 3 -1.356578 -1.996441 0.368822 -2.211478 4 -0.562777 0.518648 -2.007223 0.059411 0 0.062413 0.844813 1.853721 1.980717 1 0.539628 1.975173 0.856597 2.612406 2 1.277081 1.088457 0.152189 0.530325 3 1.356578 1.996441 0.368822 2.211478 4 0.562777 0.518648 2.007223 0.059411
2. 通过apply将函数应用到列或行上
示例代码:
print(df.apply(lambda x : x.max()))
运行结果:
注意指定轴的方向,默认axis=0,方向是列
示例代码:
print(df.apply(lambda x : x.max(), axis=1))
运行结果:
3. 通过applymap将函数应用到每个数据上
示例代码:
f2 = lambda x : '%.2f' % x
运行结果:
1 -0.54 -1.98 -0.86 -2.61
排序
1. 索引排序
sort_index()
排序默认使用升序排序,ascending=False 为降序排序
示例代码:
s4 = pd.Series(range(10, 15), index = np.random.randint(5, size=5)) s4.sort_index() # 0 0 1 3 3
运行结果:
对DataFrame操作时注意轴方向
示例代码:
df4 = pd.DataFrame(np.random.randn(3, 5), index=np.random.randint(3, size=3), columns=np.random.randint(5, size=5)) df4_isort = df4.sort_index(axis=1, ascending=False) print(df4_isort) # 4 2 1 1 0
运行结果:
2 -0.416686 -0.161256 0.088802 -0.004294 1.164138 1 -0.671914 0.531256 0.303222 -0.509493 -0.342573 1 1.988321 -0.466987 2.787891 -1.105912 0.889082 2 -0.161256 1.164138 -0.416686 -0.004294 0.088802 1 0.531256 -0.342573 -0.671914 -0.509493 0.303222 1 -0.466987 0.889082 1.988321 -1.105912 2.787891
2. 按值排序
sort_values(by='column name')
根据某个唯一的列名进行排序,如果有其他相同列名则报错。
示例代码:
df4_vsort = df4.sort_values(by=0, ascending=False)
运行结果:
1 1.988321 -0.466987 2.787891 -1.105912 0.889082 1 -0.671914 0.531256 0.303222 -0.509493 -0.342573 2 -0.416686 -0.161256 0.088802 -0.004294 1.164138
处理缺失数据
示例代码:
df_data = pd.DataFrame([np.random.randn(3), [1., 2., np.nan], [np.nan, 4., np.nan], [1., 2., 3.]])
运行结果:
0 -0.281885 -0.786572 0.487126 3 1.000000 2.000000 3.000000
1. 判断是否存在缺失值:isnull()
示例代码:
运行结果:
2. 丢弃缺失数据:dropna()
根据axis轴方向,丢弃包含NaN的行或列。 示例代码:
print(df_data.dropna(axis=1))
运行结果:
0 -0.281885 -0.786572 0.487126 3 1.000000 2.000000 3.000000
3. 填充缺失数据:fillna()
示例代码:
print(df_data.fillna(-100.))
运行结果:
0 -0.281885 -0.786572 0.487126 1 1.000000 2.000000 -100.000000 2 -100.000000 4.000000 -100.000000 3 1.000000 2.000000 3.000000
层级索引(hierarchical indexing)
下面创建一个Series, 在输入索引Index时,输入了由两个子list组成的list,第一个子list是外层索引,第二个list是内层索引。
示例代码:
ser_obj = pd.Series(np.random.randn(12),index=[ ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd'], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]
运行结果:
MultiIndex索引对象
-
打印这个Series的索引类型,显示是MultiIndex
-
直接将索引打印出来,可以看到有lavels,和labels两个信息。lavels表示两个层级中分别有那些标签,labels是每个位置分别是什么标签。
示例代码:
print(type(ser_obj.index))
运行结果:
<class 'pandas.indexes.multi.MultiIndex'> MultiIndex(levels=[['a', 'b', 'c', 'd'], [0, 1, 2]], labels=[[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]])
选取子集
1. 外层选取:
ser_obj['outer_label']
示例代码:
运行结果:
2. 内层选取:
ser_obj[:, 'inner_label']
示例代码:
运行结果:
常用于分组操作、透视表的生成等
交换分层顺序
1. swaplevel()
.swaplevel( )交换内层与外层索引。
示例代码:
print(ser_obj.swaplevel())
运行结果:
交换并排序分层
sortlevel()
.sortlevel( )先对外层索引进行排序,再对内层索引进行排序,默认是升序。
示例代码:
print(ser_obj.swaplevel().sortlevel())
运行结果:
Pandas统计计算和描述
示例代码:
df_obj = pd.DataFrame(np.random.randn(5,4), columns = ['a', 'b', 'c', 'd'])
运行结果:
0 1.469682 1.948965 1.373124 -0.564129 1 -1.466670 -0.494591 0.467787 -2.007771 2 1.368750 0.532142 0.487862 -1.130825 3 -0.758540 -0.479684 1.239135 1.073077 4 -0.007470 0.997034 2.669219 0.742070
常用的统计计算
sum, mean, max, min…
axis=0 按列统计,axis=1按行统计
skipna 排除缺失值, 默认为True
示例代码:
df_obj.min(axis=1, skipna=False)
运行结果:
常用的统计描述
describe 产生多个统计数据
示例代码:
print(df_obj.describe())
运行结果:
count 5.000000 5.000000 5.000000 5.000000 mean 0.180305 0.106488 0.244978 0.178046 std 0.641945 0.454340 1.064356 1.144416 min -0.677175 -0.490278 -1.164928 -1.574556 25% -0.064069 -0.182920 -0.464013 -0.089962 50% 0.231722 0.127846 0.355859 0.190482 75% 0.318854 0.463377 1.169750 0.983663 max 1.092195 0.614413 1.328220 1.380601
常用的统计描述方法:
Pandas分组与聚合
分组 (groupby)
示例代码:
dict_obj = {'key1' : ['a', 'b', 'a', 'b', 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randn(8), 'data2': np.random.randn(8)} df_obj = pd.DataFrame(dict_obj)
运行结果:
0 0.974685 -0.672494 a one 1 -0.214324 0.758372 b one 2 1.508838 0.392787 a two 3 0.522911 0.630814 b three 4 1.347359 -0.177858 a two 5 -0.264616 1.017155 b two 6 -0.624708 0.450885 a one 7 -1.019229 -1.143825 a three
一、GroupBy对象:DataFrameGroupBy,SeriesGroupBy
1. 分组操作
groupby()进行分组,GroupBy对象没有进行实际运算,只是包含分组的中间数据
按列名分组:obj.groupby(‘label’)
示例代码:
print(type(df_obj.groupby('key1'))) # dataframe的 data1 列根据 key1 进行分组 print(type(df_obj['data1'].groupby(df_obj['key1'])))
运行结果:
<class 'pandas.core.groupby.DataFrameGroupBy'> <class 'pandas.core.groupby.SeriesGroupBy'>
2. 分组运算
对GroupBy对象进行分组运算/多重分组运算,如mean()
非数值数据不进行分组运算
示例代码:
grouped1 = df_obj.groupby('key1') grouped2 = df_obj['data1'].groupby(df_obj['key1'])
运行结果:
Name: data1, dtype: float64
size() 返回每个分组的元素个数
示例代码:
运行结果:
3. 按自定义的key分组
obj.groupby(self_def_key)
自定义的key可为列表或多层列表
obj.groupby([‘label1’, ‘label2’])->多层dataframe
示例代码:
self_def_key = [0, 1, 2, 3, 3, 4, 5, 7] print(df_obj.groupby(self_def_key).size()) print(df_obj.groupby([df_obj['key1'], df_obj['key2']]).size()) grouped2 = df_obj.groupby(['key1', 'key2']) grouped3 = df_obj.groupby(['key2', 'key1']) # unstack可以将多层索引的结果转换成单层的dataframe print(grouped3.mean().unstack())
运行结果:
three a -1.019229 -1.143825 one 0.174988 -0.214324 -0.110804 0.758372 three -1.019229 0.522911 -1.143825 0.630814 two 1.428099 -0.264616 0.107465 1.017155
二、GroupBy对象支持迭代操作
每次迭代返回一个元组 (group_name, group_data)
可用于分组数据的具体运算
1. 单层分组
示例代码:
for group_name, group_data in grouped1:
运行结果:
0 0.974685 -0.672494 a one 2 1.508838 0.392787 a two 4 1.347359 -0.177858 a two 6 -0.624708 0.450885 a one 7 -1.019229 -1.143825 a three 1 -0.214324 0.758372 b one 3 0.522911 0.630814 b three 5 -0.264616 1.017155 b two
2. 多层分组
示例代码:
for group_name, group_data in grouped2:
运行结果:
0 0.974685 -0.672494 a one 6 -0.624708 0.450885 a one 7 -1.019229 -1.143825 a three 2 1.508838 0.392787 a two 4 1.347359 -0.177858 a two 1 -0.214324 0.758372 b one 3 0.522911 0.630814 b three 5 -0.264616 1.017155 b two
三、GroupBy对象可以转换成列表或字典
示例代码:
print(dict(list(grouped1)))
运行结果:
[('a', data1 data2 key1 key2 0 0.974685 -0.672494 a one 2 1.508838 0.392787 a two 4 1.347359 -0.177858 a two 6 -0.624708 0.450885 a one 7 -1.019229 -1.143825 a three), ('b', data1 data2 key1 key2 1 -0.214324 0.758372 b one 3 0.522911 0.630814 b three 5 -0.264616 1.017155 b two)] {'a': data1 data2 key1 key2 0 0.974685 -0.672494 a one 2 1.508838 0.392787 a two 4 1.347359 -0.177858 a two 6 -0.624708 0.450885 a one 7 -1.019229 -1.143825 a three, 'b': data1 data2 key1 key2 1 -0.214324 0.758372 b one 3 0.522911 0.630814 b three 5 -0.264616 1.017155 b two}
1. 按列分组、按数据类型分组
示例代码:
print(df_obj.groupby(df_obj.dtypes, axis=1).size()) print(df_obj.groupby(df_obj.dtypes, axis=1).sum())
运行结果:
2. 其他分组方法
示例代码:
df_obj2 = pd.DataFrame(np.random.randint(1, 10, (5,5)), columns=['a', 'b', 'c', 'd', 'e'], index=['A', 'B', 'C', 'D', 'E']) df_obj2.ix[1, 1:4] = np.NaN
运行结果:
3. 通过字典分组
示例代码:
mapping_dict = {'a':'Python', 'b':'Python', 'c':'Java', 'd':'C', 'e':'Java'} print(df_obj2.groupby(mapping_dict, axis=1).size()) print(df_obj2.groupby(mapping_dict, axis=1).count()) # 非NaN的个数 print(df_obj2.groupby(mapping_dict, axis=1).sum())
运行结果:
4. 通过函数分组,函数传入的参数为行索引或列索引
示例代码:
df_obj3 = pd.DataFrame(np.random.randint(1, 10, (5,5)), columns=['a', 'b', 'c', 'd', 'e'], index=['AA', 'BBB', 'CC', 'D', 'EE']) print(df_obj3.groupby(group_key).size()) #df_obj3.groupby(len).size()
运行结果:
5. 通过索引级别分组
示例代码:
columns = pd.MultiIndex.from_arrays([['Python', 'Java', 'Python', 'Java', 'Python'], ['A', 'A', 'B', 'C', 'B']], names=['language', 'index']) df_obj4 = pd.DataFrame(np.random.randint(1, 10, (5, 5)), columns=columns) print(df_obj4.groupby(level='language', axis=1).sum()) print(df_obj4.groupby(level='index', axis=1).sum())
运行结果:
language Python Java Python Java Python
聚合 (aggregation)
示例代码:
dict_obj = {'key1' : ['a', 'b', 'a', 'b', 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randint(1,10, 8), 'data2': np.random.randint(1,10, 8)} df_obj5 = pd.DataFrame(dict_obj)
运行结果:
1. 内置的聚合函数
sum(), mean(), max(), min(), count(), size(), describe()
示例代码:
print(df_obj5.groupby('key1').sum()) print(df_obj5.groupby('key1').max()) print(df_obj5.groupby('key1').min()) print(df_obj5.groupby('key1').mean()) print(df_obj5.groupby('key1').size()) print(df_obj5.groupby('key1').count()) print(df_obj5.groupby('key1').describe())
运行结果:
a count 5.000000 5.000000 b count 3.000000 3.000000
2. 可自定义函数,传入agg方法中
grouped.agg(func)
func的参数为groupby索引对应的记录
示例代码:
#print type(df) #参数为索引所对应的记录 return df.max() - df.min() print(df_obj5.groupby('key1').agg(peak_range)) print(df_obj.groupby('key1').agg(lambda df : df.max() - df.min()))
运行结果:
3. 应用多个聚合函数
同时应用多个函数进行聚合操作,使用函数列表
示例代码:
print(df_obj.groupby('key1').agg(['mean', 'std', 'count', peak_range])) # 默认列名为函数名 print(df_obj.groupby('key1').agg(['mean', 'std', 'count', ('range', peak_range)])) # 通过元组提供新的列名
运行结果:
mean std count peak_range mean std count peak_range a 0.437389 1.174151 5 2.528067 -0.230101 0.686488 5 1.594711 b 0.014657 0.440878 3 0.787527 0.802114 0.196850 3 0.386341 mean std count range mean std count range a 0.437389 1.174151 5 2.528067 -0.230101 0.686488 5 1.594711 b 0.014657 0.440878 3 0.787527 0.802114 0.196850 3 0.386341
4. 对不同的列分别作用不同的聚合函数,使用dict
示例代码:
dict_mapping = {'data1':'mean', print(df_obj.groupby('key1').agg(dict_mapping)) dict_mapping = {'data1':['mean','max'], print(df_obj.groupby('key1').agg(dict_mapping))
运行结果:
a 0.437389 1.508838 -1.150505 b 0.014657 0.522911 2.406341
5. 常用的内置聚合函数
数据的分组运算
示例代码:
dict_obj = {'key1' : ['a', 'b', 'a', 'b', 'key2' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'data1': np.random.randint(1, 10, 8), 'data2': np.random.randint(1, 10, 8)} df_obj = pd.DataFrame(dict_obj) # 按key1分组后,计算data1,data2的统计信息并附加到原始表格中,并添加表头前缀 k1_sum = df_obj.groupby('key1').sum().add_prefix('sum_')
运行结果:
聚合运算后会改变原始数据的形状,
如何保持原始数据的形状?
1. merge
使用merge的外连接,比较复杂
示例代码:
k1_sum_merge = pd.merge(df_obj, k1_sum, left_on='key1', right_index=True)
运行结果:
data1 data2 key1 key2 sum_data1 sum_data2
2. transform
transform的计算结果和原始数据的形状保持一致,
如:grouped.transform(np.sum)
示例代码:
k1_sum_tf = df_obj.groupby('key1').transform(np.sum).add_prefix('sum_') df_obj[k1_sum_tf.columns] = k1_sum_tf
运行结果:
data1 data2 key1 key2 sum_data1 sum_data2 sum_key2 0 5 1 a one 26 26 onetwotwoonethree 1 7 8 b one 17 17 onethreetwo 2 1 9 a two 26 26 onetwotwoonethree 3 2 6 b three 17 17 onethreetwo 4 9 8 a two 26 26 onetwotwoonethree 5 8 3 b two 17 17 onethreetwo 6 3 5 a one 26 26 onetwotwoonethree 7 8 3 a three 26 26 onetwotwoonethree
也可传入自定义函数,
示例代码:
print(df_obj.groupby('key1').transform(diff_mean))
运行结果:
data1 data2 sum_data1 sum_data2 0 -0.200000 -4.200000 0 0 6 -2.200000 -0.200000 0 0
groupby.apply(func)
func函数也可以在各分组上分别调用,最后结果通过pd.concat组装到一起(数据合并)
示例代码:
dataset_path = './starcraft.csv' df_data = pd.read_csv(dataset_path, usecols=['LeagueIndex', 'Age', 'HoursPerWeek', def top_n(df, n=3, column='APM'): 返回每个分组按 column 的 top n 数据 return df.sort_values(by=column, ascending=False)[:n] print(df_data.groupby('LeagueIndex').apply(top_n))
运行结果:
LeagueIndex Age HoursPerWeek TotalHours APM 1 2214 1 20.0 12.0 730.0 172.9530 2246 1 27.0 8.0 250.0 141.6282 1753 1 20.0 28.0 100.0 139.6362 2 3062 2 20.0 6.0 100.0 179.6250 3229 2 16.0 24.0 110.0 156.7380 1520 2 29.0 6.0 250.0 151.6470 3 1557 3 22.0 6.0 200.0 226.6554 484 3 19.0 42.0 450.0 220.0692 2883 3 16.0 8.0 800.0 208.9500 4 2688 4 26.0 24.0 990.0 249.0210 1759 4 16.0 6.0 75.0 229.9122 2637 4 23.0 24.0 650.0 227.2272 5 3277 5 18.0 16.0 950.0 372.6426 93 5 17.0 36.0 720.0 335.4990 202 5 37.0 14.0 800.0 327.7218 6 734 6 16.0 28.0 730.0 389.8314 2746 6 16.0 28.0 4000.0 350.4114 1810 6 21.0 14.0 730.0 323.2506 7 3127 7 23.0 42.0 2000.0 298.7952 104 7 21.0 24.0 1000.0 286.4538 1654 7 18.0 98.0 700.0 236.0316 8 3393 8 NaN NaN NaN 375.8664 3373 8 NaN NaN NaN 364.8504 3372 8 NaN NaN NaN 355.3518
1. 产生层级索引:外层索引是分组名,内层索引是df_obj的行索引
示例代码:
print(df_data.groupby('LeagueIndex').apply(top_n, n=2, column='Age'))
运行结果:
LeagueIndex Age HoursPerWeek TotalHours APM 1 3146 1 40.0 12.0 150.0 38.5590 3040 1 39.0 10.0 500.0 29.8764 2 920 2 43.0 10.0 730.0 86.0586 2437 2 41.0 4.0 200.0 54.2166 3 1258 3 41.0 14.0 800.0 77.6472 2972 3 40.0 10.0 500.0 60.5970 4 1696 4 44.0 6.0 500.0 89.5266 1729 4 39.0 8.0 500.0 86.7246 5 202 5 37.0 14.0 800.0 327.7218 2745 5 37.0 18.0 1000.0 123.4098 6 3069 6 31.0 8.0 800.0 133.1790 2706 6 31.0 8.0 700.0 66.9918 7 2813 7 26.0 36.0 1300.0 188.5512 1992 7 26.0 24.0 1000.0 219.6690 8 3340 8 NaN NaN NaN 189.7404 3341 8 NaN NaN NaN 287.8128
2. 禁止层级索引, group_keys=False
示例代码:
print(df_data.groupby('LeagueIndex', group_keys=False).apply(top_n))
运行结果:
LeagueIndex Age HoursPerWeek TotalHours APM 2214 1 20.0 12.0 730.0 172.9530 2246 1 27.0 8.0 250.0 141.6282 1753 1 20.0 28.0 100.0 139.6362 3062 2 20.0 6.0 100.0 179.6250 3229 2 16.0 24.0 110.0 156.7380 1520 2 29.0 6.0 250.0 151.6470 1557 3 22.0 6.0 200.0 226.6554 484 3 19.0 42.0 450.0 220.0692 2883 3 16.0 8.0 800.0 208.9500 2688 4 26.0 24.0 990.0 249.0210 1759 4 16.0 6.0 75.0 229.9122 2637 4 23.0 24.0 650.0 227.2272 3277 5 18.0 16.0 950.0 372.6426 93 5 17.0 36.0 720.0 335.4990 202 5 37.0 14.0 800.0 327.7218 734 6 16.0 28.0 730.0 389.8314 2746 6 16.0 28.0 4000.0 350.4114 1810 6 21.0 14.0 730.0 323.2506 3127 7 23.0 42.0 2000.0 298.7952 104 7 21.0 24.0 1000.0 286.4538 1654 7 18.0 98.0 700.0 236.0316 3393 8 NaN NaN NaN 375.8664 3373 8 NaN NaN NaN 364.8504 3372 8 NaN NaN NaN 355.3518
apply可以用来处理不同分组内的缺失数据填充,填充该分组的均值。
数据清洗
-
数据清洗是数据分析关键的一步,直接影响之后的处理工作
-
数据需要修改吗?有什么需要修改的吗?数据应该怎么调整才能适用于接下来的分析和挖掘?
-
是一个迭代的过程,实际项目中可能需要不止一次地执行这些清洗操作
-
处理缺失数据:pd.fillna(),pd.dropna()
数据连接(pd.merge)
示例代码:
df_obj1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1' : np.random.randint(0,10,7)}) df_obj2 = pd.DataFrame({'key': ['a', 'b', 'd'], 'data2' : np.random.randint(0,10,3)})
运行结果:
1. 默认将重叠列的列名作为“外键”进行连接
示例代码:
print(pd.merge(df_obj1, df_obj2))
运行结果:
2. on显示指定“外键”
示例代码:
print(pd.merge(df_obj1, df_obj2, on='key'))
运行结果:
3. left_on,左侧数据的“外键”,right_on,右侧数据的“外键”
示例代码:
# left_on,right_on分别指定左侧数据和右侧数据的“外键” df_obj1 = df_obj1.rename(columns={'key':'key1'}) df_obj2 = df_obj2.rename(columns={'key':'key2'}) print(pd.merge(df_obj1, df_obj2, left_on='key1', right_on='key2'))
运行结果:
默认是“内连接”(inner),即结果中的键是交集
how 指定连接方式
4. “外连接”(outer),结果中的键是并集
示例代码:
print(pd.merge(df_obj1, df_obj2, left_on='key1', right_on='key2', how='outer'))
运行结果:
5. “左连接”(left)
示例代码:
print(pd.merge(df_obj1, df_obj2, left_on='key1', right_on='key2', how='left'))
运行结果:
6. “右连接”(right)
示例代码:
print(pd.merge(df_obj1, df_obj2, left_on='key1', right_on='key2', how='right'))
运行结果:
7. 处理重复列名
suffixes,默认为_x, _y
示例代码:
df_obj1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data' : np.random.randint(0,10,7)}) df_obj2 = pd.DataFrame({'key': ['a', 'b', 'd'], 'data' : np.random.randint(0,10,3)}) print(pd.merge(df_obj1, df_obj2, on='key', suffixes=('_left', '_right')))
运行结果:
8. 按索引连接
left_index=True或right_index=True
示例代码:
df_obj1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1' : np.random.randint(0,10,7)}) df_obj2 = pd.DataFrame({'data2' : np.random.randint(0,10,3)}, index=['a', 'b', 'd']) print(pd.merge(df_obj1, df_obj2, left_on='key', right_index=True))
运行结果:
数据合并(pd.concat)
1. NumPy的concat
np.concatenate
示例代码:
arr1 = np.random.randint(0, 10, (3, 4)) arr2 = np.random.randint(0, 10, (3, 4)) print(np.concatenate([arr1, arr2])) print(np.concatenate([arr1, arr2], axis=1))
运行结果:
# print(np.concatenate([arr1, arr2])) # print(np.concatenate([arr1, arr2], axis=1))
2. pd.concat
-
注意指定轴方向,默认axis=0
-
join指定合并方式,默认为outer
-
Series合并时查看行索引有无重复
1) index 没有重复的情况
示例代码:
ser_obj1 = pd.Series(np.random.randint(0, 10, 5), index=range(0,5)) ser_obj2 = pd.Series(np.random.randint(0, 10, 4), index=range(5,9)) ser_obj3 = pd.Series(np.random.randint(0, 10, 3), index=range(9,12)) print(pd.concat([ser_obj1, ser_obj2, ser_obj3])) print(pd.concat([ser_obj1, ser_obj2, ser_obj3], axis=1))
运行结果:
# print(pd.concat([ser_obj1, ser_obj2, ser_obj3])) # print(pd.concat([ser_obj1, ser_obj2, ser_obj3], axis=1))
2) index 有重复的情况
示例代码:
ser_obj1 = pd.Series(np.random.randint(0, 10, 5), index=range(5)) ser_obj2 = pd.Series(np.random.randint(0, 10, 4), index=range(4)) ser_obj3 = pd.Series(np.random.randint(0, 10, 3), index=range(3)) print(pd.concat([ser_obj1, ser_obj2, ser_obj3]))
运行结果:
# print(pd.concat([ser_obj1, ser_obj2, ser_obj3])) # print(pd.concat([ser_obj1, ser_obj2, ser_obj3], axis=1, join='inner')) # join='inner' 将去除NaN所在的行或列
3) DataFrame合并时同时查看行索引和列索引有无重复
示例代码:
df_obj1 = pd.DataFrame(np.random.randint(0, 10, (3, 2)), index=['a', 'b', 'c'], df_obj2 = pd.DataFrame(np.random.randint(0, 10, (2, 2)), index=['a', 'b'], print(pd.concat([df_obj1, df_obj2])) print(pd.concat([df_obj1, df_obj2], axis=1, join='inner'))
运行结果:
# print(pd.concat([df_obj1, df_obj2])) # print(pd.concat([df_obj1, df_obj2], axis=1, join='inner'))
数据重构
1. stack
-
将列索引旋转为行索引,完成层级索引
-
DataFrame->Series
示例代码:
df_obj = pd.DataFrame(np.random.randint(0,10, (5,2)), columns=['data1', 'data2'])
运行结果:
2. unstack
-
将层级索引展开
-
Series->DataFrame
-
认操作内层索引,即level=-1
示例代码:
print(stacked.unstack(level=0))
运行结果:
# print(stacked.unstack()) # print(stacked.unstack(level=0))
数据转换
一、 处理重复数据
1 duplicated() 返回布尔型Series表示每行是否为重复行
示例代码:
df_obj = pd.DataFrame({'data1' : ['a'] * 4 + ['b'] * 4, 'data2' : np.random.randint(0, 4, 8)}) print(df_obj.duplicated())
运行结果:
# print(df_obj.duplicated())
2 drop_duplicates() 过滤重复行
默认判断全部列
可指定按某些列判断
示例代码:
print(df_obj.drop_duplicates()) print(df_obj.drop_duplicates('data2'))
运行结果:
# print(df_obj.drop_duplicates()) # print(df_obj.drop_duplicates('data2'))
3. 根据map 传入的函数对每行或每列进行转换
- Series根据
map 传入的函数对每行或每列进行转换
示例代码:
ser_obj = pd.Series(np.random.randint(0,10,10)) print(ser_obj.map(lambda x : x ** 2))
运行结果:
# print(ser_obj.map(lambda x : x ** 2))
二、数据替换
replace 根据值的内容进行替换
示例代码:
print(ser_obj.replace(1, -100)) print(ser_obj.replace([6, 8], -100)) print(ser_obj.replace([4, 7], [-100, -200]))
运行结果:
# print(ser_obj.replace(1, -100)) # print(ser_obj.replace([6, 8], -100)) # print(ser_obj.replace([4, 7], [-100, -200]))
聚类模型:K-Means
K-Means算法
算法思想:
以空间中k个样本点为中心进行聚类,对最靠近它们的样本点归类。通过迭 代的方法,逐步更新各聚类中心,直至达到最好的聚类效果
算法描述:
- 选择k个聚类的初始中心
- 在第n次迭代中,对任意一个样本点,求其到k个聚类中心的距离,将该 样本点归类到距离最小的中心所在的聚类
- 利用均值等方法更新各类的中心值
- 对所有的k个聚类中心,如果利用2,3步的迭代更新后,达到稳定,则迭代 结束。
优缺点:
全球食品数据分析
项目参考:https://www./bhouwens/d/openfoodfacts/world-food-facts/how-much-sugar-do-we-eat/discussion
import matplotlib.pyplot as plt def unzip(zip_filepath, dest_path): with zipfile.ZipFile(zip_filepath) as zf: zf.extractall(path=dest_path) def get_dataset_filename(zip_filepath): with zipfile.ZipFile(zip_filepath) as zf: dataset_path = './data' # 数据集路径 zip_filename = 'open-food-facts.zip' # zip文件名 zip_filepath = os.path.join(dataset_path, zip_filename) # zip文件路径 dataset_filename = get_dataset_filename(zip_filepath) # 数据集文件名(在zip中) dataset_filepath = os.path.join(dataset_path, dataset_filename) # 数据集文件路径 print('解压zip...', end='') unzip(zip_filepath, dataset_path) data = pd.read_csv(dataset_filepath, usecols=['countries_en', 'additives_n']) data = data.dropna() # 或者data.dropna(inplace=True) # 课后练习:经过观察发现'countries_en'中的数值不是单独的国家名称, # 有的是多个国家名称用逗号隔开,如 Albania,Belgium,France,Germany,Italy,Netherlands,Spain # 正确的统计应该是将这些值拆开成多个行记录,然后进行分组统计 data['countries_en'] = data['countries_en'].str.lower() country_additives = data['additives_n'].groupby(data['countries_en']).mean() result = country_additives.sort_values(ascending=False) result.iloc[:10].plot.bar() result.to_csv('./country_additives.csv') if os.path.exists(dataset_filepath): os.remove(dataset_filepath) if __name__ == '__main__':
|