本文共 50762 字,大约阅读时间需要 169 分钟。
目录
Pandas的名称来自于面板数据(panel data)和Python数据分析(data analysis)。
Pandas是一个强大的分析结构化数据的工具集,基于NumPy构建,提供了 高级数据结构 和 数据操作工具,它是使Python成为强大而高效的数据分析环境的重要因素之一。
一个强大的分析和操作大型结构化数据集所需的工具集
基础是NumPy,提供了高性能矩阵的运算
提供了大量能够快速便捷地处理数据的函数和方法
应用于数据挖掘,数据分析
提供数据清洗功能
数据存在csv中,直接使用pd. read_csv即可
对于数据库比如mysql或者mongodb中数据:pd.read_sql(sql_sentence,connection)
DataFrame对象既有行索引,又有列索引
行索引,表明不同行,横向索引,叫index,0轴,axis=0
列索引,表名不同列,纵向索引,叫columns,1轴,axis=1
df.sort_values(by="Count_AnimalName",ascending=False) #排序
df_sorted = df.sort_values(by="Count_AnimalName")
df_sorted[:100]
那么问题来了:
我们具体要选择某一列该怎么选择呢?df[" Count_AnimalName "]
我们要同时选择行和列改怎么办?df[:100][" Count_AnimalName "]
还有更多的经过pandas优化过的选择方式:
1.df.loc 通过标签索引行数据
2.df.iloc 通过位置获取行数据
赋值更改数据的过程:
我们的数据缺失通常有两种情况:
一种就是空,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会
join:默认情况下他是把行索引相同的数据合并到一起
默认的合并方式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")})
pd.date_range(start=None, end=None, periods=None, freq='D')
start和end以及freq配合能够生成start和end范围内以频率freq的一组时间索引
start和periods以及freq配合能够生成从start开始的频率为freq的periods个时间索引
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提供了一个resample的方法来帮助我们实现频率转化
之前所学习的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()import pandas as pd
Pandas有两个最主要也是最重要的数据结构: Series 和 DataFrame
Series
Series是一种类似于一维数组的 对象,由一组数据(各种NumPy数据类型)以及一组与之对应的索引(数据标签)组成。
1. 通过list构建Series
ser_obj = pd.Series(range(10))
示例代码:
# 通过list构建Seriesser_obj = pd.Series(range(10, 20))print(ser_obj.head(3))print(ser_obj)print(type(ser_obj))
运行结果:
0 101 112 12dtype: int640 101 112 123 134 145 156 167 178 189 19dtype: int64
2. 获取数据和索引
ser_obj.index 和 ser_obj.values
示例代码:
# 获取数据print(ser_obj.values)# 获取索引print(ser_obj.index)
运行结果:
[10 11 12 13 14 15 16 17 18 19]RangeIndex(start=0, stop=10, step=1)
3. 通过索引获取数据
ser_obj[idx]
示例代码:
#通过索引获取数据print(ser_obj[0])print(ser_obj[8])
运行结果:
1018
4. 索引与数据的对应关系不被运算结果影响
示例代码:
# 索引与数据的对应关系不被运算结果影响print(ser_obj * 2)print(ser_obj > 15)
运行结果:
0 201 222 243 264 285 306 327 348 369 38dtype: int640 False1 False2 False3 False4 False5 False6 True7 True8 True9 Truedtype: bool
5. 通过dict构建Series
示例代码:
# 通过dict构建Seriesyear_data = {2001: 17.8, 2002: 20.1, 2003: 16.5}ser_obj2 = pd.Series(year_data)print(ser_obj2.head())print(ser_obj2.index)
运行结果:
2001 17.82002 20.12003 16.5dtype: float64Int64Index([2001, 2002, 2003], dtype='int64')
name属性
对象名:ser_obj.name
对象索引名:ser_obj.index.name
示例代码:
# name属性ser_obj2.name = 'temp'ser_obj2.index.name = 'year'print(ser_obj2.head())
运行结果:
year2001 17.82002 20.12003 16.5Name: temp, dtype: float64
DataFrame
DataFrame是一个表格型的数据结构,它含有一组有序的列,每列可以是不同类型的值。DataFrame既有行索引也有列索引,它可以被看做是由Series组成的字典(共用同一个索引),数据是以二维结构存放的。
1. 通过ndarray构建DataFrame
示例代码:
import numpy as np# 通过ndarray构建DataFramearray = np.random.randn(5,4)print(array)df_obj = pd.DataFrame(array)print(df_obj.head())
运行结果:
[[ 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 1 2 30 0.835006 -1.492901 -0.531201 -0.1131391 0.646298 -0.367799 0.080111 0.6008052 -1.234585 0.334097 -0.587782 -0.7361063 -1.476514 0.994002 0.210020 -0.9051574 0.566694 1.382383 -0.490990 1.944846
2. 通过dict构建DataFrame
示例代码:
# 通过dict构建DataFramedict_data = {'A': 1, '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"], 'F': 'ITCast' }#print dict_datadf_obj2 = pd.DataFrame(dict_data)print(df_obj2)
运行结果:
A B C D E F0 1 2017-04-26 1.0 3 Python ITCast1 1 2017-04-26 1.0 3 Java ITCast2 1 2017-04-26 1.0 3 C++ ITCast3 1 2017-04-26 1.0 3 C ITCast
3. 通过列索引获取列数据(Series类型)
df_obj[col_idx] 或 df_obj.col_idx
示例代码:
# 通过列索引获取列数据print(df_obj2['A'])print(type(df_obj2['A']))print(df_obj2.A)
运行结果:
0 1.01 1.02 1.03 1.0Name: A, dtype: float640 1.01 1.02 1.03 1.0Name: A, dtype: float64
4. 增加列数据
df_obj[new_col_idx] = data
类似Python的 dict添加key-value
示例代码:
# 增加列df_obj2['G'] = df_obj2['D'] + 4print(df_obj2.head())
运行结果:
A B C D E F G0 1.0 2017-01-02 1.0 3 Python ITCast 71 1.0 2017-01-02 1.0 3 Java ITCast 72 1.0 2017-01-02 1.0 3 C++ ITCast 73 1.0 2017-01-02 1.0 3 C ITCast 7
5. 删除列
del df_obj[col_idx]
示例代码:
# 删除列del(df_obj2['G'] )print(df_obj2.head())
运行结果:
A B C D E F0 1.0 2017-01-02 1.0 3 Python ITCast1 1.0 2017-01-02 1.0 3 Java ITCast2 1.0 2017-01-02 1.0 3 C++ ITCast3 1.0 2017-01-02 1.0 3 C ITCast
索引对象Index
1. Series和DataFrame中的索引都是Index对象
示例代码:
print(type(ser_obj.index))print(type(df_obj2.index))print(df_obj2.index)
运行结果:
Int64Index([0, 1, 2, 3], dtype='int64')
2. 索引对象不可变,保证了数据的安全
示例代码:
# 索引对象不可变df_obj2.index[0] = 2
运行结果:
---------------------------------------------------------------------------TypeError Traceback (most recent call last)in () 1 # 索引对象不可变----> 2 df_obj2.index[0] = 2/Users/Power/anaconda/lib/python3.6/site-packages/pandas/indexes/base.py in __setitem__(self, key, value) 1402 1403 def __setitem__(self, key, value):-> 1404 raise TypeError("Index does not support mutable operations") 1405 1406 def __getitem__(self, key):TypeError: Index does not support mutable operations
常见的Index种类
Series索引
1. index 指定行索引名
示例代码:
ser_obj = pd.Series(range(5), index = ['a', 'b', 'c', 'd', 'e'])print(ser_obj.head())
运行结果:
a 0b 1c 2d 3e 4dtype: int64
2. 行索引
ser_obj[‘label’], ser_obj[pos]
示例代码:
# 行索引print(ser_obj['b'])print(ser_obj[2])
运行结果:
12
3. 切片索引
ser_obj[2:4], ser_obj[‘label1’: ’label3’]
注意,按索引名切片操作时,是包含终止索引的。
示例代码:
# 切片索引print(ser_obj[1:3])print(ser_obj['b':'d'])
运行结果:
b 1c 2dtype: int64b 1c 2d 3dtype: int64
4. 不连续索引
ser_obj[[‘label1’, ’label2’, ‘label3’]]
示例代码:
# 不连续索引print(ser_obj[[0, 2, 4]])print(ser_obj[['a', 'e']])
运行结果:
a 0c 2e 4dtype: int64a 0e 4dtype: int64
5. 布尔索引
示例代码:
# 布尔索引ser_bool = ser_obj > 2print(ser_bool)print(ser_obj[ser_bool])print(ser_obj[ser_obj > 2])
运行结果:
a Falseb Falsec Falsed Truee Truedtype: boold 3e 4dtype: int64d 3e 4dtype: int64
1. columns 指定列索引名
示例代码:
import numpy as npdf_obj = pd.DataFrame(np.random.randn(5,4), columns = ['a', 'b', 'c', 'd'])print(df_obj.head())
运行结果:
a b c d0 -0.241678 0.621589 0.843546 -0.3831051 -0.526918 -0.485325 1.124420 -0.6531442 -1.074163 0.939324 -0.309822 -0.2091493 -0.716816 1.844654 -2.123637 -1.3234844 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类型
运行结果:
0 -0.2416781 -0.5269182 -1.0741633 -0.7168164 0.368212Name: a, dtype: float64
3. 不连续索引
df_obj[[‘label1’, ‘label2’]]
示例代码:
# 不连续索引print(df_obj[['a','c']])print(df_obj[[1, 3]])
运行结果:
a c0 -0.241678 0.8435461 -0.526918 1.1244202 -1.074163 -0.3098223 -0.716816 -2.1236374 0.368212 0.064703 b d0 0.621589 -0.3831051 -0.485325 -0.6531442 0.939324 -0.2091493 1.844654 -1.3234844 -0.910324 0.486016
高级索引:标签、位置和混合
Pandas的高级索引有3种
1. loc 标签索引
DataFrame 不能直接切片,可以通过loc来做切片
loc是基于标签名的索引,也就是我们自定义的索引名
示例代码:
# 标签索引 loc# Seriesprint(ser_obj['b':'d'])print(ser_obj.loc['b':'d'])# DataFrameprint(df_obj['a'])# 第一个参数索引行,第二个参数是列print(df_obj.loc[0:2, 'a'])
运行结果:
b 1c 2d 3dtype: int64b 1c 2d 3dtype: int640 -0.2416781 -0.5269182 -1.0741633 -0.7168164 0.368212Name: a, dtype: float640 -0.2416781 -0.5269182 -1.074163Name: a, dtype: float64
2. iloc 位置索引
作用和loc一样,不过是基于索引编号来索引
示例代码:
# 整型位置索引 iloc# Seriesprint(ser_obj[1:3])print(ser_obj.iloc[1:3])# DataFrameprint(df_obj.iloc[0:2, 0]) # 注意和df_obj.loc[0:2, 'a']的区别
运行结果:
b 1c 2dtype: int64b 1c 2dtype: int640 -0.2416781 -0.526918Name: a, dtype: float64
3. ix 标签与位置混合索引
ix是以上二者的综合,既可以使用索引编号,又可以使用自定义索引,要视情况不同来使用,
如果索引既有数字又有英文,那么这种方式是不建议使用的,容易导致定位的混乱。
示例代码:
# 混合索引 ix# Seriesprint(ser_obj.ix[1:3])print(ser_obj.ix['b':'c'])# DataFrameprint(df_obj.loc[0:2, 'a'])print(df_obj.ix[0:2, 0])
运行结果:
b 1c 2dtype: int64b 1c 2dtype: int640 -0.2416781 -0.5269182 -1.074163Name: a, dtype: float64
注意
DataFrame索引操作,可将其看作ndarray的索引操作
标签的切片索引是包含末尾位置的
是数据清洗的重要过程,可以按索引对齐进行运算,如果没对齐的位置则补NaN,最后也可以填充NaN
Series的对齐运算
1. Series 按行、索引对齐
示例代码:
s1 = pd.Series(range(10, 20), index = range(10))s2 = pd.Series(range(20, 25), index = range(5))print('s1: ' )print(s1)print('') print('s2: ')print(s2)
运行结果:
s1: 0 101 112 123 134 145 156 167 178 189 19dtype: int64s2: 0 201 212 223 234 24dtype: int64
2. Series的对齐运算
示例代码:
# Series 对齐运算s1 + s2
运行结果:
0 30.01 32.02 34.03 36.04 38.05 NaN6 NaN7 NaN8 NaN9 NaNdtype: float64
DataFrame的对齐运算
1. DataFrame按行、列索引对齐
示例代码:
df1 = pd.DataFrame(np.ones((2,2)), columns = ['a', 'b'])df2 = pd.DataFrame(np.ones((3,3)), columns = ['a', 'b', 'c'])print('df1: ')print(df1)print('') print('df2: ')print(df2)
运行结果:
df1: a b0 1.0 1.01 1.0 1.0df2: a b c0 1.0 1.0 1.01 1.0 1.0 1.02 1.0 1.0 1.0
2. DataFrame的对齐运算
示例代码:
# DataFrame对齐操作df1 + df2
运行结果:
a b c0 2.0 2.0 NaN1 2.0 2.0 NaN2 NaN NaN NaN
填充未对齐的数据进行运算
1. fill_value
使用
add
,sub
,div
,mul
的同时,通过
fill_value
指定填充值,未对齐的数据将和填充值做运算
示例代码:
print(s1)print(s2)s1.add(s2, fill_value = -1)print(df1)print(df2)df1.sub(df2, fill_value = 2.)
运行结果:
# print(s1)0 101 112 123 134 145 156 167 178 189 19dtype: int64# print(s2)0 201 212 223 234 24dtype: int64# s1.add(s2, fill_value = -1)0 30.01 32.02 34.03 36.04 38.05 14.06 15.07 16.08 17.09 18.0dtype: float64# print(df1) a b0 1.0 1.01 1.0 1.0# print(df2) a b c0 1.0 1.0 1.01 1.0 1.0 1.02 1.0 1.0 1.0# df1.sub(df2, fill_value = 2.) a b c0 0.0 0.0 1.01 0.0 0.0 1.02 1.0 1.0 1.0
apply 和 applymap
1. 可直接使用NumPy的函数
示例代码:
# Numpy ufunc 函数df = pd.DataFrame(np.random.randn(5,4) - 1)print(df)print(np.abs(df))
运行结果:
0 1 2 30 -0.062413 0.844813 -1.853721 -1.9807171 -0.539628 -1.975173 -0.856597 -2.6124062 -1.277081 -1.088457 -0.152189 0.5303253 -1.356578 -1.996441 0.368822 -2.2114784 -0.562777 0.518648 -2.007223 0.059411 0 1 2 30 0.062413 0.844813 1.853721 1.9807171 0.539628 1.975173 0.856597 2.6124062 1.277081 1.088457 0.152189 0.5303253 1.356578 1.996441 0.368822 2.2114784 0.562777 0.518648 2.007223 0.059411
2. 通过apply将函数应用到列或行上
示例代码:
# 使用apply应用行或列数据#f = lambda x : x.max()print(df.apply(lambda x : x.max()))
运行结果:
0 -0.0624131 0.8448132 0.3688223 0.530325dtype: float64
注意指定轴的方向,默认axis=0,方向是列
示例代码:
# 指定轴方向,axis=1,方向是行print(df.apply(lambda x : x.max(), axis=1))
运行结果:
0 0.8448131 -0.5396282 0.5303253 0.3688224 0.518648dtype: float64
3. 通过applymap将函数应用到每个数据上
示例代码:
# 使用applymap应用到每个数据f2 = lambda x : '%.2f' % xprint(df.applymap(f2))
运行结果:
0 1 2 30 -0.06 0.84 -1.85 -1.981 -0.54 -1.98 -0.86 -2.612 -1.28 -1.09 -0.15 0.533 -1.36 -2.00 0.37 -2.214 -0.56 0.52 -2.01 0.06
排序
1. 索引排序
sort_index()
排序默认使用升序排序,ascending=False 为降序排序
示例代码:
# Seriess4 = pd.Series(range(10, 15), index = np.random.randint(5, size=5))print(s4)# 索引排序s4.sort_index() # 0 0 1 3 3
运行结果:
0 103 111 123 130 14dtype: int640 100 141 123 113 13dtype: int64
对DataFrame操作时注意轴方向
示例代码:
# DataFramedf4 = pd.DataFrame(np.random.randn(3, 5), index=np.random.randint(3, size=3), columns=np.random.randint(5, size=5))print(df4)df4_isort = df4.sort_index(axis=1, ascending=False)print(df4_isort) # 4 2 1 1 0
运行结果:
1 4 0 1 22 -0.416686 -0.161256 0.088802 -0.004294 1.1641381 -0.671914 0.531256 0.303222 -0.509493 -0.3425731 1.988321 -0.466987 2.787891 -1.105912 0.889082 4 2 1 1 02 -0.161256 1.164138 -0.416686 -0.004294 0.0888021 0.531256 -0.342573 -0.671914 -0.509493 0.3032221 -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)print(df4_vsort)
运行结果:
1 4 0 1 21 1.988321 -0.466987 2.787891 -1.105912 0.8890821 -0.671914 0.531256 0.303222 -0.509493 -0.3425732 -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.]])print(df_data.head())
运行结果:
0 1 20 -0.281885 -0.786572 0.4871261 1.000000 2.000000 NaN2 NaN 4.000000 NaN3 1.000000 2.000000 3.000000
1. 判断是否存在缺失值:isnull()
示例代码:
# isnullprint(df_data.isnull())
运行结果:
0 1 20 False False False1 False False True2 True False True3 False False False
2. 丢弃缺失数据:dropna()
根据axis轴方向,丢弃包含NaN的行或列。 示例代码:
# dropnaprint(df_data.dropna())print(df_data.dropna(axis=1))
运行结果:
0 1 20 -0.281885 -0.786572 0.4871263 1.000000 2.000000 3.000000 10 -0.7865721 2.0000002 4.0000003 2.000000
3. 填充缺失数据:fillna()
示例代码:
# fillnaprint(df_data.fillna(-100.))
运行结果:
0 1 20 -0.281885 -0.786572 0.4871261 1.000000 2.000000 -100.0000002 -100.000000 4.000000 -100.0000003 1.000000 2.000000 3.000000
下面创建一个Series, 在输入索引Index时,输入了由两个子list组成的list,第一个子list是外层索引,第二个list是内层索引。
示例代码:
import pandas as pdimport numpy as npser_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] ])print(ser_obj)
运行结果:
a 0 0.099174 1 -0.310414 2 -0.558047b 0 1.742445 1 1.152924 2 -0.725332c 0 -0.150638 1 0.251660 2 0.063387d 0 1.080605 1 0.567547 2 -0.154148dtype: float64
MultiIndex索引对象
打印这个Series的索引类型,显示是MultiIndex
直接将索引打印出来,可以看到有lavels,和labels两个信息。lavels表示两个层级中分别有那些标签,labels是每个位置分别是什么标签。
示例代码:
print(type(ser_obj.index))print(ser_obj.index)
运行结果:
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]])
选取子集
根据索引获取数据。因为现在有两层索引,当通过外层索引获取数据的时候,可以直接利用外层索引的标签来获取。
当要通过内层索引获取数据的时候,在list中传入两个元素,前者是表示要选取的外层索引,后者表示要选取的内层索引。
1. 外层选取:
ser_obj['outer_label']
示例代码:
# 外层选取print(ser_obj['c'])
运行结果:
0 -1.3620961 1.5580912 -0.452313dtype: float64
2. 内层选取:
ser_obj[:, 'inner_label']
示例代码:
# 内层选取print(ser_obj[:, 2])
运行结果:
a 0.826662b 0.015426c -0.452313d -0.051063dtype: float64
常用于分组操作、透视表的生成等
交换分层顺序
1. swaplevel()
.swaplevel( )交换内层与外层索引。
示例代码:
print(ser_obj.swaplevel())
运行结果:
0 a 0.0991741 a -0.3104142 a -0.5580470 b 1.7424451 b 1.1529242 b -0.7253320 c -0.1506381 c 0.2516602 c 0.0633870 d 1.0806051 d 0.5675472 d -0.154148dtype: float64
交换并排序分层
sortlevel()
.sortlevel( )先对外层索引进行排序,再对内层索引进行排序,默认是升序。
示例代码:
# 交换并排序分层print(ser_obj.swaplevel().sortlevel())
运行结果:
0 a 0.099174 b 1.742445 c -0.150638 d 1.0806051 a -0.310414 b 1.152924 c 0.251660 d 0.5675472 a -0.558047 b -0.725332 c 0.063387 d -0.154148dtype: float64
示例代码:
import numpy as npimport pandas as pddf_obj = pd.DataFrame(np.random.randn(5,4), columns = ['a', 'b', 'c', 'd'])print(df_obj)
运行结果:
a b c d0 1.469682 1.948965 1.373124 -0.5641291 -1.466670 -0.494591 0.467787 -2.0077712 1.368750 0.532142 0.487862 -1.1308253 -0.758540 -0.479684 1.239135 1.0730774 -0.007470 0.997034 2.669219 0.742070
常用的统计计算
sum, mean, max, min…
axis=0 按列统计,axis=1按行统计
skipna 排除缺失值, 默认为True
示例代码:
df_obj.sum()df_obj.max()df_obj.min(axis=1, skipna=False)
运行结果:
a 0.605751b 2.503866c 6.237127d -1.887578dtype: float64a 1.469682b 1.948965c 2.669219d 1.073077dtype: float640 -0.5641291 -2.0077712 -1.1308253 -0.7585404 -0.007470dtype: float64
常用的统计描述
describe 产生多个统计数据
示例代码:
print(df_obj.describe())
运行结果:
a b c dcount 5.000000 5.000000 5.000000 5.000000mean 0.180305 0.106488 0.244978 0.178046std 0.641945 0.454340 1.064356 1.144416min -0.677175 -0.490278 -1.164928 -1.57455625% -0.064069 -0.182920 -0.464013 -0.08996250% 0.231722 0.127846 0.355859 0.19048275% 0.318854 0.463377 1.169750 0.983663max 1.092195 0.614413 1.328220 1.380601
常用的统计描述方法:
分组 (groupby)
对数据集进行分组,然后对每组进行统计分析
SQL能够对数据进行过滤,分组聚合
pandas能利用groupby进行更加复杂的分组运算
分组运算过程:split->apply->combine
拆分:进行分组的根据
应用:每个分组运行的计算规则
合并:把每个分组的计算结果合并起来
示例代码:
import pandas as pdimport numpy as npdict_obj = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], '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)print(df_obj)
运行结果:
data1 data2 key1 key20 0.974685 -0.672494 a one1 -0.214324 0.758372 b one2 1.508838 0.392787 a two3 0.522911 0.630814 b three4 1.347359 -0.177858 a two5 -0.264616 1.017155 b two6 -0.624708 0.450885 a one7 -1.019229 -1.143825 a three
1. 分组操作
groupby()进行分组,GroupBy对象没有进行实际运算,只是包含分组的中间数据
按列名分组:obj.groupby(‘label’)
示例代码:
# dataframe根据key1进行分组print(type(df_obj.groupby('key1')))# dataframe的 data1 列根据 key1 进行分组print(type(df_obj['data1'].groupby(df_obj['key1'])))
运行结果:
2. 分组运算
对GroupBy对象进行分组运算/多重分组运算,如mean()
非数值数据不进行分组运算
示例代码:
# 分组运算grouped1 = df_obj.groupby('key1')print(grouped1.mean())grouped2 = df_obj['data1'].groupby(df_obj['key1'])print(grouped2.mean())
运行结果:
data1 data2key1 a 0.437389 -0.230101b 0.014657 0.802114key1a 0.437389b 0.014657Name: data1, dtype: float64
size() 返回每个分组的元素个数
示例代码:
# sizeprint(grouped1.size())print(grouped2.size())
运行结果:
key1a 5b 3dtype: int64key1a 5b 3dtype: int64
3. 按自定义的key分组
obj.groupby(self_def_key)
自定义的key可为列表或多层列表
obj.groupby([‘label1’, ‘label2’])->多层dataframe
示例代码:
# 按自定义key分组,列表self_def_key = [0, 1, 2, 3, 3, 4, 5, 7]print(df_obj.groupby(self_def_key).size())# 按自定义key分组,多层列表print(df_obj.groupby([df_obj['key1'], df_obj['key2']]).size())# 按多个列多层分组grouped2 = df_obj.groupby(['key1', 'key2'])print(grouped2.size())# 多层分组按key的顺序进行grouped3 = df_obj.groupby(['key2', 'key1'])print(grouped3.mean())# unstack可以将多层索引的结果转换成单层的dataframeprint(grouped3.mean().unstack())
运行结果:
0 11 12 13 24 15 17 1dtype: int64key1 key2 a one 2 three 1 two 2b one 1 three 1 two 1dtype: int64key1 key2 a one 2 three 1 two 2b one 1 three 1 two 1dtype: int64 data1 data2key2 key1 one a 0.174988 -0.110804 b -0.214324 0.758372three a -1.019229 -1.143825 b 0.522911 0.630814two a 1.428099 0.107465 b -0.264616 1.017155 data1 data2 key1 a b a bkey2 one 0.174988 -0.214324 -0.110804 0.758372three -1.019229 0.522911 -1.143825 0.630814two 1.428099 -0.264616 0.107465 1.017155
每次迭代返回一个元组 (group_name, group_data)
可用于分组数据的具体运算
1. 单层分组
示例代码:
# 单层分组,根据key1for group_name, group_data in grouped1: print(group_name) print(group_data)
运行结果:
a data1 data2 key1 key20 0.974685 -0.672494 a one2 1.508838 0.392787 a two4 1.347359 -0.177858 a two6 -0.624708 0.450885 a one7 -1.019229 -1.143825 a threeb data1 data2 key1 key21 -0.214324 0.758372 b one3 0.522911 0.630814 b three5 -0.264616 1.017155 b two
2. 多层分组
示例代码:
# 多层分组,根据key1 和 key2for group_name, group_data in grouped2: print(group_name) print(group_data)
运行结果:
('a', 'one') data1 data2 key1 key20 0.974685 -0.672494 a one6 -0.624708 0.450885 a one('a', 'three') data1 data2 key1 key27 -1.019229 -1.143825 a three('a', 'two') data1 data2 key1 key22 1.508838 0.392787 a two4 1.347359 -0.177858 a two('b', 'one') data1 data2 key1 key21 -0.214324 0.758372 b one('b', 'three') data1 data2 key1 key23 0.522911 0.630814 b three('b', 'two') data1 data2 key1 key25 -0.264616 1.017155 b two
三、GroupBy对象可以转换成列表或字典
示例代码:
# GroupBy对象转换listprint(list(grouped1))# GroupBy对象转换dictprint(dict(list(grouped1)))
运行结果:
[('a', data1 data2 key1 key20 0.974685 -0.672494 a one2 1.508838 0.392787 a two4 1.347359 -0.177858 a two6 -0.624708 0.450885 a one7 -1.019229 -1.143825 a three), ('b', data1 data2 key1 key21 -0.214324 0.758372 b one3 0.522911 0.630814 b three5 -0.264616 1.017155 b two)]{'a': data1 data2 key1 key20 0.974685 -0.672494 a one2 1.508838 0.392787 a two4 1.347359 -0.177858 a two6 -0.624708 0.450885 a one7 -1.019229 -1.143825 a three, 'b': data1 data2 key1 key21 -0.214324 0.758372 b one3 0.522911 0.630814 b three5 -0.264616 1.017155 b two}
1. 按列分组、按数据类型分组
示例代码:
# 按列分组print(df_obj.dtypes)# 按数据类型分组print(df_obj.groupby(df_obj.dtypes, axis=1).size())print(df_obj.groupby(df_obj.dtypes, axis=1).sum())
运行结果:
data1 float64data2 float64key1 objectkey2 objectdtype: objectfloat64 2object 2dtype: int64 float64 object0 0.302191 a one1 0.544048 b one2 1.901626 a two3 1.153725 b three4 1.169501 a two5 0.752539 b two6 -0.173823 a one7 -2.163054 a three
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.NaNprint(df_obj2)
运行结果:
a b c d eA 7 2.0 4.0 5.0 8B 4 NaN NaN NaN 1C 3 2.0 5.0 4.0 6D 3 1.0 9.0 7.0 3E 6 1.0 6.0 8.0 1
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())
运行结果:
C 1Java 2Python 2dtype: int64 C Java PythonA 1 2 2B 0 1 1C 1 2 2D 1 2 2E 1 2 2 C Java PythonA 5.0 12.0 9.0B NaN 1.0 4.0C 4.0 11.0 5.0D 7.0 12.0 4.0E 8.0 7.0 7.0
4. 通过函数分组,函数传入的参数为行索引或列索引
示例代码:
# 通过函数分组df_obj3 = pd.DataFrame(np.random.randint(1, 10, (5,5)), columns=['a', 'b', 'c', 'd', 'e'], index=['AA', 'BBB', 'CC', 'D', 'EE'])#df_obj3def group_key(idx): """ idx 为列索引或行索引 """ #return idx return len(idx)print(df_obj3.groupby(group_key).size())# 以上自定义函数等价于#df_obj3.groupby(len).size()
运行结果:
1 12 33 1dtype: int64
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)# 根据language进行分组print(df_obj4.groupby(level='language', axis=1).sum())# 根据index进行分组print(df_obj4.groupby(level='index', axis=1).sum())
运行结果:
language Python Java Python Java Pythonindex A A B C B0 2 7 8 4 31 5 2 6 1 22 6 4 4 5 23 4 7 4 3 14 7 4 3 4 8language Java Python0 11 131 3 132 9 123 10 94 8 18index A B C0 9 11 41 7 8 12 10 6 53 11 5 34 11 11 4
聚合 (aggregation)
数组产生标量的过程,如mean()、count()等
常用于对分组后的数据进行计算
示例代码:
dict_obj = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], '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)print(df_obj5)
运行结果:
data1 data2 key1 key20 3 7 a one1 1 5 b one2 7 4 a two3 2 4 b three4 6 4 a two5 9 9 b two6 3 5 a one7 8 4 a three
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())
运行结果:
data1 data2key1 a 27 24b 12 18 data1 data2 key2key1 a 8 7 twob 9 9 two data1 data2 key2key1 a 3 4 oneb 1 4 one data1 data2key1 a 5.4 4.8b 4.0 6.0key1a 5b 3dtype: int64 data1 data2 key2key1 a 5 5 5b 3 3 3 data1 data2key1 a count 5.000000 5.000000 mean 5.400000 4.800000 std 2.302173 1.303840 min 3.000000 4.000000 25% 3.000000 4.000000 50% 6.000000 4.000000 75% 7.000000 5.000000 max 8.000000 7.000000b count 3.000000 3.000000 mean 4.000000 6.000000 std 4.358899 2.645751 min 1.000000 4.000000 25% 1.500000 4.500000 50% 2.000000 5.000000 75% 5.500000 7.000000 max 9.000000 9.000000
2. 可自定义函数,传入agg方法中
grouped.agg(func)
func的参数为groupby索引对应的记录
示例代码:
# 自定义聚合函数def peak_range(df): """ 返回数值范围 """ #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()))
运行结果:
data1 data2key1 a 5 3b 8 5 data1 data2key1 a 2.528067 1.594711b 0.787527 0.386341In [25]:
3. 应用多个聚合函数
同时应用多个函数进行聚合操作,使用函数列表
示例代码:
# 应用多个聚合函数# 同时应用多个聚合函数print(df_obj.groupby('key1').agg(['mean', 'std', 'count', peak_range])) # 默认列名为函数名print(df_obj.groupby('key1').agg(['mean', 'std', 'count', ('range', peak_range)])) # 通过元组提供新的列名
运行结果:
data1 data2 mean std count peak_range mean std count peak_rangekey1 a 0.437389 1.174151 5 2.528067 -0.230101 0.686488 5 1.594711b 0.014657 0.440878 3 0.787527 0.802114 0.196850 3 0.386341 data1 data2 mean std count range mean std count rangekey1 a 0.437389 1.174151 5 2.528067 -0.230101 0.686488 5 1.594711b 0.014657 0.440878 3 0.787527 0.802114 0.196850 3 0.386341
4. 对不同的列分别作用不同的聚合函数,使用dict
示例代码:
# 每列作用不同的聚合函数dict_mapping = {'data1':'mean', 'data2':'sum'}print(df_obj.groupby('key1').agg(dict_mapping))dict_mapping = {'data1':['mean','max'], 'data2':'sum'}print(df_obj.groupby('key1').agg(dict_mapping))
运行结果:
data1 data2key1 a 0.437389 -1.150505b 0.014657 2.406341 data1 data2 mean max sumkey1 a 0.437389 1.508838 -1.150505b 0.014657 0.522911 2.406341
5. 常用的内置聚合函数
数据的分组运算
示例代码:
import pandas as pdimport numpy as npdict_obj = {'key1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'a'], '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)print(df_obj)# 按key1分组后,计算data1,data2的统计信息并附加到原始表格中,并添加表头前缀k1_sum = df_obj.groupby('key1').sum().add_prefix('sum_')print(k1_sum)
运行结果:
data1 data2 key1 key20 5 1 a one1 7 8 b one2 1 9 a two3 2 6 b three4 9 8 a two5 8 3 b two6 3 5 a one7 8 3 a three sum_data1 sum_data2key1 a 26 26b 17 17
聚合运算后会改变原始数据的形状,
如何保持原始数据的形状?
1. merge
使用merge的外连接,比较复杂
示例代码:
# 方法1,使用mergek1_sum_merge = pd.merge(df_obj, k1_sum, left_on='key1', right_index=True)print(k1_sum_merge)
运行结果:
data1 data2 key1 key2 sum_data1 sum_data20 5 1 a one 26 262 1 9 a two 26 264 9 8 a two 26 266 3 5 a one 26 267 8 3 a three 26 261 7 8 b one 17 173 2 6 b three 17 175 8 3 b two 17 17
2. transform
transform的计算结果和原始数据的形状保持一致,
如:grouped.transform(np.sum)
示例代码:
# 方法2,使用transformk1_sum_tf = df_obj.groupby('key1').transform(np.sum).add_prefix('sum_')df_obj[k1_sum_tf.columns] = k1_sum_tfprint(df_obj)
运行结果:
data1 data2 key1 key2 sum_data1 sum_data2 sum_key20 5 1 a one 26 26 onetwotwoonethree1 7 8 b one 17 17 onethreetwo2 1 9 a two 26 26 onetwotwoonethree3 2 6 b three 17 17 onethreetwo4 9 8 a two 26 26 onetwotwoonethree5 8 3 b two 17 17 onethreetwo6 3 5 a one 26 26 onetwotwoonethree7 8 3 a three 26 26 onetwotwoonethree
也可传入自定义函数,
示例代码:
# 自定义函数传入transformdef diff_mean(s): """ 返回数据与均值的差值 """ return s - s.mean()print(df_obj.groupby('key1').transform(diff_mean))
运行结果:
data1 data2 sum_data1 sum_data20 -0.200000 -4.200000 0 01 1.333333 2.333333 0 02 -4.200000 3.800000 0 03 -3.666667 0.333333 0 04 3.800000 2.800000 0 05 2.333333 -2.666667 0 06 -2.200000 -0.200000 0 07 2.800000 -2.200000 0 0
groupby.apply(func)
func函数也可以在各分组上分别调用,最后结果通过pd.concat组装到一起(数据合并)
示例代码:
import pandas as pdimport numpy as npdataset_path = './starcraft.csv'df_data = pd.read_csv(dataset_path, usecols=['LeagueIndex', 'Age', 'HoursPerWeek', 'TotalHours', 'APM'])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 APMLeagueIndex 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.63622 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.64703 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.95004 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.22725 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.72186 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.25067 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.03168 3393 8 NaN NaN NaN 375.8664 3373 8 NaN NaN NaN 364.8504 3372 8 NaN NaN NaN 355.3518
1. 产生层级索引:外层索引是分组名,内层索引是df_obj的行索引
示例代码:
# apply函数接收的参数会传入自定义的函数中print(df_data.groupby('LeagueIndex').apply(top_n, n=2, column='Age'))
运行结果:
LeagueIndex Age HoursPerWeek TotalHours APMLeagueIndex 1 3146 1 40.0 12.0 150.0 38.5590 3040 1 39.0 10.0 500.0 29.87642 920 2 43.0 10.0 730.0 86.0586 2437 2 41.0 4.0 200.0 54.21663 1258 3 41.0 14.0 800.0 77.6472 2972 3 40.0 10.0 500.0 60.59704 1696 4 44.0 6.0 500.0 89.5266 1729 4 39.0 8.0 500.0 86.72465 202 5 37.0 14.0 800.0 327.7218 2745 5 37.0 18.0 1000.0 123.40986 3069 6 31.0 8.0 800.0 133.1790 2706 6 31.0 8.0 700.0 66.99187 2813 7 26.0 36.0 1300.0 188.5512 1992 7 26.0 24.0 1000.0 219.66908 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 APM2214 1 20.0 12.0 730.0 172.95302246 1 27.0 8.0 250.0 141.62821753 1 20.0 28.0 100.0 139.63623062 2 20.0 6.0 100.0 179.62503229 2 16.0 24.0 110.0 156.73801520 2 29.0 6.0 250.0 151.64701557 3 22.0 6.0 200.0 226.6554484 3 19.0 42.0 450.0 220.06922883 3 16.0 8.0 800.0 208.95002688 4 26.0 24.0 990.0 249.02101759 4 16.0 6.0 75.0 229.91222637 4 23.0 24.0 650.0 227.22723277 5 18.0 16.0 950.0 372.642693 5 17.0 36.0 720.0 335.4990202 5 37.0 14.0 800.0 327.7218734 6 16.0 28.0 730.0 389.83142746 6 16.0 28.0 4000.0 350.41141810 6 21.0 14.0 730.0 323.25063127 7 23.0 42.0 2000.0 298.7952104 7 21.0 24.0 1000.0 286.45381654 7 18.0 98.0 700.0 236.03163393 8 NaN NaN NaN 375.86643373 8 NaN NaN NaN 364.85043372 8 NaN NaN NaN 355.3518
apply可以用来处理不同分组内的缺失数据填充,填充该分组的均值。
数据清洗是数据分析关键的一步,直接影响之后的处理工作
数据需要修改吗?有什么需要修改的吗?数据应该怎么调整才能适用于接下来的分析和挖掘?
是一个迭代的过程,实际项目中可能需要不止一次地执行这些清洗操作
处理缺失数据:pd.fillna(),pd.dropna()
数据连接(pd.merge)
pd.merge
根据单个或多个键将不同DataFrame的行连接起来
类似数据库的连接操作
示例代码:
import pandas as pdimport numpy as npdf_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)})print(df_obj1)print(df_obj2)
运行结果:
data1 key data1 key0 8 b1 8 b2 3 a3 5 c4 4 a5 9 a6 6 b data2 key0 9 a1 0 b2 3 d
1. 默认将重叠列的列名作为“外键”进行连接
示例代码:
# 默认将重叠列的列名作为“外键”进行连接print(pd.merge(df_obj1, df_obj2))
运行结果:
data1 key data20 8 b 01 8 b 02 6 b 03 3 a 94 4 a 95 9 a 9
2. on显示指定“外键”
示例代码:
# on显示指定“外键”print(pd.merge(df_obj1, df_obj2, on='key'))
运行结果:
data1 key data20 8 b 01 8 b 02 6 b 03 3 a 94 4 a 95 9 a 9
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'))
运行结果:
data1 key1 data2 key20 8 b 0 b1 8 b 0 b2 6 b 0 b3 3 a 9 a4 4 a 9 a5 9 a 9 a
默认是“内连接”(inner),即结果中的键是交集
how
指定连接方式
4. “外连接”(outer),结果中的键是并集
示例代码:
# “外连接”print(pd.merge(df_obj1, df_obj2, left_on='key1', right_on='key2', how='outer'))
运行结果:
data1 key1 data2 key20 8.0 b 0.0 b1 8.0 b 0.0 b2 6.0 b 0.0 b3 3.0 a 9.0 a4 4.0 a 9.0 a5 9.0 a 9.0 a6 5.0 c NaN NaN7 NaN NaN 3.0 d
5. “左连接”(left)
示例代码:
# 左连接print(pd.merge(df_obj1, df_obj2, left_on='key1', right_on='key2', how='left'))
运行结果:
data1 key1 data2 key20 8 b 0.0 b1 8 b 0.0 b2 3 a 9.0 a3 5 c NaN NaN4 4 a 9.0 a5 9 a 9.0 a6 6 b 0.0 b
6. “右连接”(right)
示例代码:
# 右连接print(pd.merge(df_obj1, df_obj2, left_on='key1', right_on='key2', how='right'))
运行结果:
data1 key1 data2 key20 8.0 b 0 b1 8.0 b 0 b2 6.0 b 0 b3 3.0 a 9 a4 4.0 a 9 a5 9.0 a 9 a6 NaN NaN 3 d
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')))
运行结果:
data_left key data_right0 9 b 11 5 b 12 1 b 13 2 a 84 2 a 85 5 a 8
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))
运行结果:
data1 key data20 3 b 61 4 b 66 8 b 62 6 a 04 3 a 05 0 a 0
数据合并(pd.concat)
1. NumPy的concat
np.concatenate
示例代码:
import numpy as npimport pandas as pdarr1 = np.random.randint(0, 10, (3, 4))arr2 = np.random.randint(0, 10, (3, 4))print(arr1)print(arr2)print(np.concatenate([arr1, arr2]))print(np.concatenate([arr1, arr2], axis=1))
运行结果:
# print(arr1)[[3 3 0 8] [2 0 3 1] [4 8 8 2]]# print(arr2)[[6 8 7 3] [1 6 8 7] [1 4 7 1]]# print(np.concatenate([arr1, arr2])) [[3 3 0 8] [2 0 3 1] [4 8 8 2] [6 8 7 3] [1 6 8 7] [1 4 7 1]]# print(np.concatenate([arr1, arr2], axis=1)) [[3 3 0 8 6 8 7 3] [2 0 3 1 1 6 8 7] [4 8 8 2 1 4 7 1]]
2. pd.concat
注意指定轴方向,默认axis=0
join指定合并方式,默认为outer
Series合并时查看行索引有无重复
1) index 没有重复的情况
示例代码:
# 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(ser_obj1)print(ser_obj2)print(ser_obj3)print(pd.concat([ser_obj1, ser_obj2, ser_obj3]))print(pd.concat([ser_obj1, ser_obj2, ser_obj3], axis=1))
运行结果:
# print(ser_obj1)0 11 82 43 94 4dtype: int64# print(ser_obj2)5 26 67 48 2dtype: int64# print(ser_obj3)9 610 211 7dtype: int64# print(pd.concat([ser_obj1, ser_obj2, ser_obj3]))0 11 82 43 94 45 26 67 48 29 610 211 7dtype: int64# print(pd.concat([ser_obj1, ser_obj2, ser_obj3], axis=1)) 0 1 20 1.0 NaN NaN1 5.0 NaN NaN2 3.0 NaN NaN3 2.0 NaN NaN4 4.0 NaN NaN5 NaN 9.0 NaN6 NaN 8.0 NaN7 NaN 3.0 NaN8 NaN 6.0 NaN9 NaN NaN 2.010 NaN NaN 3.011 NaN NaN 3.0
2) index 有重复的情况
示例代码:
# 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(ser_obj1)print(ser_obj2)print(ser_obj3)print(pd.concat([ser_obj1, ser_obj2, ser_obj3]))
运行结果:
# print(ser_obj1)0 01 32 73 24 5dtype: int64# print(ser_obj2)0 51 12 93 9dtype: int64# print(ser_obj3)0 81 72 9dtype: int64# print(pd.concat([ser_obj1, ser_obj2, ser_obj3]))0 01 32 73 24 50 51 12 93 90 81 72 9dtype: int64# print(pd.concat([ser_obj1, ser_obj2, ser_obj3], axis=1, join='inner')) # join='inner' 将去除NaN所在的行或列 0 1 20 0 5 81 3 1 72 7 9 9
3) DataFrame合并时同时查看行索引和列索引有无重复
示例代码:
df_obj1 = pd.DataFrame(np.random.randint(0, 10, (3, 2)), index=['a', 'b', 'c'], columns=['A', 'B'])df_obj2 = pd.DataFrame(np.random.randint(0, 10, (2, 2)), index=['a', 'b'], columns=['C', 'D'])print(df_obj1)print(df_obj2)print(pd.concat([df_obj1, df_obj2]))print(pd.concat([df_obj1, df_obj2], axis=1, join='inner'))
运行结果:
# print(df_obj1) A Ba 3 3b 5 4c 8 6# print(df_obj2) C Da 1 9b 6 8# print(pd.concat([df_obj1, df_obj2])) A B C Da 3.0 3.0 NaN NaNb 5.0 4.0 NaN NaNc 8.0 6.0 NaN NaNa NaN NaN 1.0 9.0b NaN NaN 6.0 8.0# print(pd.concat([df_obj1, df_obj2], axis=1, join='inner')) A B C Da 3 3 1 9b 5 4 6 8
数据重构
1. stack
将列索引旋转为行索引,完成层级索引
DataFrame->Series
示例代码:
import numpy as npimport pandas as pddf_obj = pd.DataFrame(np.random.randint(0,10, (5,2)), columns=['data1', 'data2'])print(df_obj)stacked = df_obj.stack()print(stacked)
运行结果:
# print(df_obj) data1 data20 7 91 7 82 8 93 4 14 1 2# print(stacked)0 data1 7 data2 91 data1 7 data2 82 data1 8 data2 93 data1 4 data2 14 data1 1 data2 2dtype: int64
2. unstack
将层级索引展开
Series->DataFrame
认操作内层索引,即level=-1
示例代码:
# 默认操作内层索引print(stacked.unstack())# 通过level指定操作索引的级别print(stacked.unstack(level=0))
运行结果:
# print(stacked.unstack()) data1 data20 7 91 7 82 8 93 4 14 1 2# print(stacked.unstack(level=0)) 0 1 2 3 4data1 7 7 8 4 1data2 9 8 9 1 2
数据转换
1 duplicated()
返回布尔型Series表示每行是否为重复行
示例代码:
import numpy as npimport pandas as pddf_obj = pd.DataFrame({'data1' : ['a'] * 4 + ['b'] * 4, 'data2' : np.random.randint(0, 4, 8)})print(df_obj)print(df_obj.duplicated())
运行结果:
# print(df_obj) data1 data20 a 31 a 22 a 33 a 34 b 15 b 06 b 37 b 0# print(df_obj.duplicated())0 False1 False2 True3 True4 False5 False6 False7 Truedtype: bool
2 drop_duplicates()
过滤重复行
默认判断全部列
可指定按某些列判断
示例代码:
print(df_obj.drop_duplicates())print(df_obj.drop_duplicates('data2'))
运行结果:
# print(df_obj.drop_duplicates()) data1 data20 a 31 a 24 b 15 b 06 b 3# print(df_obj.drop_duplicates('data2')) data1 data20 a 31 a 24 b 15 b 0
3. 根据map
传入的函数对每行或每列进行转换
map
传入的函数对每行或每列进行转换示例代码:
ser_obj = pd.Series(np.random.randint(0,10,10))print(ser_obj)print(ser_obj.map(lambda x : x ** 2))
运行结果:
# print(ser_obj)0 11 42 83 64 85 66 67 48 79 3dtype: int64# print(ser_obj.map(lambda x : x ** 2))0 11 162 643 364 645 366 367 168 499 9dtype: int64
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))0 -1001 42 83 64 85 66 67 48 79 3dtype: int64# print(ser_obj.replace([6, 8], -100))0 11 42 -1003 -1004 -1005 -1006 -1007 48 79 3dtype: int64# print(ser_obj.replace([4, 7], [-100, -200]))0 11 -1002 83 64 85 66 67 -1008 -2009 3dtype: int64
聚类(clustering)属于无监督学习(unsupervised learning)
无类别标记
在线 demo:
数据挖掘十大经典算法之一
算法接收参数k;然后将样本点划分为k个聚类;同一聚类中的样本相似度较高;不同聚类中的样本相似度较小
以空间中k个样本点为中心进行聚类,对最靠近它们的样本点归类。通过迭 代的方法,逐步更新各聚类中心,直至达到最好的聚类效果
优点:速度快,简单
缺点:最终结果和初始点的选择相关,容易陷入局部最优,需要给定k值
项目参考:
# -*- coding : utf-8 -*-# 处理zip压缩文件import zipfileimport osimport pandas as pdimport matplotlib.pyplot as pltdef unzip(zip_filepath, dest_path): """ 解压zip文件 """ 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: return zf.namelist()[0]def run_main(): """ 主函数 """ # 声明变量 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) print('完成.') # 读取数据 data = pd.read_csv(dataset_filepath, usecols=['countries_en', 'additives_n']) # 分析各国家食物中的食品添加剂种类个数 # 1. 数据清理 # 去除缺失数据 data = data.dropna() # 或者data.dropna(inplace=True) # 将国家名称转换为小写 # 课后练习:经过观察发现'countries_en'中的数值不是单独的国家名称, # 有的是多个国家名称用逗号隔开,如 Albania,Belgium,France,Germany,Italy,Netherlands,Spain # 正确的统计应该是将这些值拆开成多个行记录,然后进行分组统计 data['countries_en'] = data['countries_en'].str.lower() # 2. 数据分组统计 country_additives = data['additives_n'].groupby(data['countries_en']).mean() # 3. 按值从大到小排序 result = country_additives.sort_values(ascending=False) # 4. pandas可视化top10 result.iloc[:10].plot.bar() plt.show() # 5. 保存处理结果 result.to_csv('./country_additives.csv') # 删除解压数据,清理空间 if os.path.exists(dataset_filepath): os.remove(dataset_filepath)if __name__ == '__main__': run_main()