系列文章
机器学习入门 01 —— 机器学习概述 机器学习入门 02 —— 环境搭建(Jupyter Notebook 及扩展库的安装与使用) 机器学习入门 03 —— Matplotlib使用 机器学习入门 04 —— Numpy使用 机器学习入门 05 —— Pandas使用
5 Pandas
学习目标:
5.1 Pandas介绍
?
- 是专门用于数据挖掘的开源Python库
- 以Numpy为基础,利用了Numpy模块在计算方面高性能的优势
- 基于Matplotlib,能够简便快速地画图
- 有着独特的数据结构
有了numpy和matplotlib,为什么还要使用Pandas?
?
5.2 Pandas数据结构
Pandas中有三种数据结构:
- Series:对应一维数组
- DataFrame:对应二维数组
- MultiIndex(Panel):对应三维数组
?
1 Series
Series是类似于一维数组的数据结构,它能存储任何类型的数据。Series由索引和数据构成。
?
创建Series
import pandas as pd
pd.Series(data=None, index=None, dtype=None)
data:传入的数据,可以是ndarry、列表、字典、元祖等。
index:索引,要求是唯一值,且数量和data一一对应。如果没有传入索引,则默认从0开始。如果data是字典,索引就是key。
dtype:数据类型。
?
Series属性
?
2 DataFrame
DataFrame类似于二维数组,它就像一个表格,所以既有行索引又有列索引。
?
创建DataFrame
import pandas as pd
pd.DataFrame(data=None, index=None, columns=None)
index:行标签。如果没有传入,则默认创建0-N作为索引。
columns:列标签。如果没有传入,则默认创建0-N作为索引。
?
DataFrame属性
?
重置索引
reset_index(drop=False) 设置新的下标索引(0-N)。drop如果为True表示要删除原来的索引值。
?
设置某列值为新索引
set_index(keys, drop=True) 设置keys为新索引,drop默认为True,删除原索引。
上面把年份和月份都设置为索引,这其实就是一个MultiIndex(多重索引)
?
3 MultiIndex
MultiIndex是三维的数据结构,多重索引,其实就是在Series、DataFrame对象上拥有两个或两个以上的索引的结构。
我们打印下刚刚的年月份的索引:
创建MultiIndex
?
5.3 基本数据操作
为了更好的理解这些基本操作,这里的例子会先读取一个真实的文件数据。(关于文件操作后面会介绍,这里看看就行)
data = pd.read_csv('./stock_day.csv')
data = data.drop(["ma5","ma10","ma20","v_ma5","v_ma10","v_ma20"], axis=1)
data.head(3)
?
1 索引操作
直接操作
要特别注意,Pandas直接操作索引是先列后行,和列表、Numpy的先行后列不同!!
data['open']['2018-02-27']
data['2018-02-27']['open']
data[:1][:2]
loc和iloc
data.loc['2018-02-27':'2018-02-23','open':'close']
data.iloc[:4, :3]
data.loc[data.index[0:4], ['open', 'close', 'high', 'low']]
data.iloc[0:4, data.columns.get_indexer(['open', 'close', 'high', 'low'])]
?
2 赋值操作
data.close = 1
data.loc['2018-02-27', 'close'] = 2
data[data.iloc[:,3]>22]=0
data.head(3)
?
3 排序
Series和DataFrame都可以进行排序,根据索引排序,也可以根据值排序。
DataFrame排序
sort_values(by=keys,ascending=True) :根据值排序。by是排序参考的键(可以多个),ascending为True是升序(默认)。sort_index(ascending=True) :根据索引排序。升序。
data.sort_values('open', ascending=True).head(3)
data.sort_values(['open','high'], ascending=False).head(3)
data.sort_index().head(3)
Series排序
由于Series只有一列,所以不需要传入参数,要么根据值排序要么根据索引排序。
sort_values(ascending=True) :根据值排序。ascending=True升序(默认)。sort_index(ascending=True) :根据索引排序。
data['open'].sort_values(ascending=True).head(3)
data['open'].sort_index(ascending=False).head(3)
?
5.4 运算
1 算数运算
data['open'].add(10)
data['open'].sub(10)
data['open'].mul(10)
data['open'].div(10)
?
2 逻辑运算
a = data['open'] > 23
data[a]
data[(data['open'] > 23) & (data['open'] < 24) ]
data.query('open > 23 & open < 24')
b = data['open'].isin([23.53, 23.85])
data[b]
?
3 统计运算
下面的函数可以对Series和DataFrame操作。对于DataFrame的统计函数,都有参数axis ,axis=0 表示对列进行统计(默认),axis=1 表示对行进行统计。
统计函数 | 描述 |
---|
sum() | 求和 | mean() | 平均值 | median() | 中位数(是从小到大的中间一位数字) | min() | 最小值 | max() | 最大值 | mode() | 众数(出现次数最多的) | abs() | 绝对值 | prod() | 标准差 | std() | 标准差 | var() | 方差 | idxmax() | 最大值的索引 | idxmin() | 最小值的索引 | cumsum() | 对某一列累计求和 | cummax() | 对某一列累计求最大值 | cummin() | 对某一列累计求最小值 | cumprod() | 对某一列累计求积 |
?
4 自定义运算
可以自己定义函数进行运算
'''
apply(function, axis=0)
function:自定义的函数;axis:0 对列,1对行。
'''
def function(x):
return x.max() - x.min()
data[['open', 'close']].apply(lambda x: x.max() - x.min())
?
5.5 画图
Pandas中封装了Matplotlib的画图,所以用法大致相同。在Pandas中使用画图函数plot ,同时,需要先导入Matplotlib。
无论是DataFrame还是Series都是使用plot() 画图。
DataFrame.plot(kind='line') :
- kind=‘line’,表示要画折线图(默认)
- kind=‘bar’,柱状图,加上参数stacked=True就是堆积柱状图
- kind=‘barh’,水平方向柱状图
- kind=‘hist’,直方图
- kind=‘pie’,饼图
- kind=‘scatter’,散点图
下面举个简单例子:
import matplotlib.pyplot as pltimport pandas as pd
?
5.6 文件读取与存储
我们的数据大部分都存储在文件中,而Pandas支持多种文件操作,例如CSV、HDF5、JSON、SQL、XLS等。
最常用的是HDF5和CSV。优先选择HDF5:
- HDF5在存储的时候支持压缩,使用的方式是blosc,这个是速度最快的也是pandas默认支持的
- 使用压缩可以提磁盘利用率,节省空间
- HDF5还是跨平台的,可以轻松迁移到hadoop 上面
下面这张表是常用API
?
1 CSV
'''
读取CSV
pandas.read_csv(filepath_or_buffer, sep=',', usecols)
filepath_or_buffer:文件路径
sep:分隔符,默认用,
usecols:指定读取的列名(用列表类型)
'''
data = pd.read_csv('./Pandas测试数据.csv', usecols=['open', 'close'])
'''
写入csv
DataFrame.to_csv(path_or_buf=None, sep=',', columns=None, header=True, index=True, mode='w', encoding=None)
path_of_buf:文件路径
sep:分隔符
columns:要写入的列索引
header:是否写入【列索引】
index:是否写入【行索引】
mode: 'w'重写,'a'追加
encoding:编码格式
'''
data[:10].to_csv('test.csv', columns=['open'],index=False)
?
2 HDF5
'''
写入HDF5
DataFrame.to_hdf(path_or_buf, key)
path_or_buffer:文件路径
key:读取的键(HDF5的读取和存储都要指定一个key)
'''
data.to_hdf('hdf5_data.hdf', 'HDF5_DATA')
'''
读取HDF5(读取需要导入tables模块)
pandas.read_hdf(path_or_buf, key=None)
path_or_buffer:文件路径
key:读取的键(HDF5的读取和存储都要指定一个key)
'''
data = pd.read_hdf('hdf5_data.hdf','HDF5_DATA')
?
3 JSON
JSON存储形式有几种:
- ‘split’:将索引、列名、数据三种分开。形如
{index -> [index], columns -> [columns], data -> [values]} - ’records’:形如
columns:values ,通常用这种。 - ‘index’:形如
index:{columns:values}... - ’columns’:形如
columns:{index:values} - ‘values’:直接输出值
'''
JSON的存储
DataFrame.to_json(path_or_buf=None, orient=None, lines=False)
path_or_buf:文件路径
orient:存储JSON的形式 【'split'、'records','index','colummns','values'】
lines:一个对象存储一行(建议设置为True)
'''
data.to_json('jsondata.json', orient='records')
'''
pandas.read_json(path_or_buf=None, orient=None, typ='frame', lines=False)
typ : default ‘frame’, 指定转换成的对象类型series或者dataframe
'''
pd.read_json('jsondata.json',orient='records')
?
5.7 缺失值处理
我们获取到的数据不一定都是完整的,可能某个数据有缺失,所以我们需要先对错误数据进行处理。
- 缺失的表现形式可能是
NaN 或者? 之类的,需要我们先查看数据进行判断。 - 如果缺失值的标记方式是
NaN
- 用来判断
NaN 的函数:pd.isnull(DataFrame) 、pd.notnull(DataFrame) - 存在缺失值:
- 如果缺失值数量少,则删除。数量多,则替换。
- 删除缺失值(不会改变原表,只返回新表):
dropna(axis='rows)' - 替换缺失值:
fillna(value, inplace=True) ,其中value替换的值,inplace=True会修改原表。 - 如果缺失值不是
NaN 而是? 之类的
下面进行举例说明:
- 判断缺失值是否存在
- 删除缺失值NaN
- 替换缺失值NaN
- 对于
非NaN 的缺失值,例如? :这是下面的链接
?
5.8 数据离散化
1 数据离散化介绍
为什么要数据离散化
连续数据离散化是为了简化数据结构,数据离散化技术可以用来减少给定连续属性值的个数。离散化方法经常作为数据挖掘的工具。
?
什么是数据离散化
就是在连续数据的值域上,将值域划分为若干个离散的区间,最后用不同的符号或整数值代表落在每个子区间中的属性值。下面举个栗子:
- 原始人的身高数据:165,174,160,180,159,163,192,184
- 假设按照身高分几个区间段:150~165, 165~180, 180~195
- 我们将数据分到了三个区间段,每个数据可以对应到矮、中、高三个类别的区间,最终要处理成一个"哑变量"矩阵(后面有解释)
?
One-Hot编码
在很多学习任务中,特征并不总是连续值,而有可能是分类值。
离散特征的编码分为两种情况:
1、离散特征的取值之间没有大小的意义,比如color:[red,blue],那么就使用one-hot编码
2、离散特征的取值有大小的意义,比如size:[X,XL,XXL],那么就使用数值的映射{X:1,XL:2,XXL:3}
One-Hot编码又称为独热编码(或哑变量 dummy variable),我们把数据离散化后的每个类别区间转为布尔列,这些列中只有一个可以为True(1)。例如下面这种表格,把【Human、Penguin、Octopus、Alien】分为了四个布尔列。
?
2 API介绍
pd.qcut(data, q) :返回Series。对数据进行分组(区间是自动分配的),q是分组个数。通常会再搭配value_counts() 用来统计每组里数据个数。pd.cut(data, bins) :返回Series。也是对数据分组,但区间由自己指定。bins是分组区间。pd.get_dummines(data, prefix=None) :data是Series或者DataFrame,prefix是分组的名称。
下面举例:
data = pd.read_csv('stock_day.csv')
p_change = data['p_change']
qcut = pd.qcut(p_change, 10)
qcut.value_counts()
?
bins = [-100, -7, -5, -3, 0, 3, 5, 7, 100]
cut = pd.cut(p_change, bins)
cut.value_counts()
?
pd.get_dummies(cut).head()
?
5.9 表格合并
有时候我们会想讲多张表格合并在一起,就需要用到pd.concat() 或者pd.merge()
pd.concat([data1, data2], axis=1) ,第一个参数是表格数据的列表,第二个参数为1则按行索引合并,为0则按列索引合并。
data = pd.read_csv('stock_day.csv')
bins = [-100, -7, -5, -3, 0, 3, 5, 7, 100]
cut = pd.cut(p_change, bins)
dummies = pd.get_dummies(cut, prefix='rise')
pd.concat([data, dummies], axis=1)
pd.merge(left, right, how='inner', on=None)
- 可以指定安装两组数据的共同键值对合并或按照左右各自合并。
- left:左表(DataFrame)
- right:右表(DataFrame)
- on:指定共同的键(如果不指定就是左右表各自合并)
- how:按什么方式连接(和数据库的表连接类似,inner、left、right、outer)
left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3'],
'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1']})
right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3'],
'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0']})
内连接:
pd.merge(left, right)
左连接:
pd.merge(left, right, how='left', on=['key1', 'key2'])
右连接:
pd.merge(left, right, how='right', on=['key1', 'key2'])
外连接:
pd.merge(left, right, how='outer', on=['key1', 'key2'])
?
5.10 交叉表与透视表
交叉表:用于统计两列数据之间的关系。
透视表:是将原有的DataFrame的列分别作为行索引和列索引,然后对指定的列应用聚集函数【pd.crosstab(列1,列2) 】
下面用案例进行说明:探究股票涨跌与星期之间的关系(交叉表)。【data.pivot_table([列1..], 索引) 】
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
data = pd.read_csv('stock_day.csv')
week = pd.to_datetime(data.index).weekday
data['week'] = week
data['p_n'] = np.where(data['p_change'] > 0, 1, 0)
count = pd.crosstab(data['week'], data['p_n'])
img = count.div(count.sum(axis=1).astype(np.float32), axis=0)
img.plot(kind='bar', stacked=True)
plt.show()
图1:
图2:
图3:
从上面可看出,交叉表是统计出两列的数量关系,而透视表是直接得出百分比关系。
data.pivot_table(['p_n'], 'week')
?
5.11 分组与聚合
分组与聚合通常是分析数据的一种方式,通常与一些统计函数一起使用,查看数据的分组情况。其实刚才的交叉表与透视表也有分组的功能,所以算是分组的一种形式,只不过他们主要是计算次数或者计算比例。下面这张图就非常形象。
聚合的内置函数:sum(), mean(), max(), min(), count(), size(), describe()
下面用代码演示:
星巴克案例:
starbucks = pd.read_csv("directory.csv")
count = starbucks.groupby(['Country']).count()
count['Brand'].pl ot(kind='bar', figsize=(20, 8))
plt.show()
starbucks.groupby(['Country', 'State/Province']).count().head()
?
5.12 练习案例-电影分析
现在对2006年至2016年1000部流行电影进行分析:
- 获取电影评分的平均分和导演人数
- 获取电影评分和时长的分布情况
- 获取电影分类情况
先导包并读取电影数据。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
movie = pd.read_csv('IMDB-Movie-Data.csv')
movie.head(3)
?
1 获取电影评分的平均分和导演人数
movie['Rating'].mean()
movie['Director'].unique().shape[0]
np.unique(movie['Director']).shape[0]
?
2 获取电影评分和时长的分布情况
如果使用Pandas直接绘制直方图,会出现坐标无法对齐的问题,所以还是要使用Matplotlib。
movie['Rating'].plot(kind='hist', figsize=(14, 6))
?
plt.figure(figsize=(20,8),dpi=80)
plt.hist(movie["Rating"].values,bins=20)
max_rating = movie['Rating'].max()
min_rating = movie['Rating'].min()
x_tick = np.linspace(min_rating, max_rating, num=21)
plt.xticks(x_tick)
plt.grid()
plt.show()
?
3 获取电影分类情况
思路:
- 创建一个全为0的表(即DataFrame),表的列索引就是电影类别(要去重)
- 然后遍历所有电影,由于每部电影可能有多个类别,所以在遍历一部电影时,根据其类别使其列值加1
- 对每一列求和,这样就得到每个类别的电影总数
temp_list = [i.split(',') for i in movie['Genre']]
genre_list = np.unique([i for j in temp_list for i in j])
temp_df = pd.DataFrame(np.zeros([movie.shape[0], genre_list.shape[0]]), columns=genre_list)
for i in range(movie.shape[0]):
temp_df.loc[i, temp_list[i]] = 1
result = temp_df.sum().sort_values()
result.plot(kind='bar', figsize=(15, 6), fontsize=20, colormap='cool')
|