文章中所包含的数据:
提取码:um6i
Numpy入门
数据科学领域5个常用的python库
Numpy
- N维数组(矩阵),快速高效,矢量数学运算
- 高效的Index,不需要循环
- 开源免费跨平台,运行效率足以和C/Matlab媲美
Scipy
- 依赖于Numpy
- 专为科学和工程设计
- 实现了多种常用科学计算:线性代数,傅里叶变换,信号和图像处理
Pandas
- 结构化数据分析利器(依赖Numpy )
- 提供了多种高级数据结构:Time-Series(时间索引),DataFrame, Panel
- 强大的数据索引和处理能力
Matplotlib
- Python 2D绘图领域使用最广泛的套件
- 基本能取代Matlab的绘图功能(散点,曲线,柱形等)
- 通过mplot3d可以绘制精美的3D图
Scikit-learn
- 机器学习的Python模块
- 建立在Scipy之上,提供了常用的机器学习算法∶聚类,回归
- 简单易学的API接口
数学基础回顾之矩阵运算
定义
- 矩阵∶矩形的数组,即二维数组。其中向量和标量都是矩阵的特例
- 向量:是指1xn或者nx1的矩阵
- 标量:1x1的矩阵
- 数组:N维的数组,是矩阵的延伸
特殊矩阵
全0全1矩阵
单位矩阵
对角线上的元素全为1,其余元素为0
矩阵的加减运算
- 相加,减的两个矩阵必须要有相同的行和列
- 行和列对应元素相加减
数组的乘法(点乘)
数组乘法(点乘)是对应元素之间的乘法
矩阵的乘法(叉乘)
设A为mxp的矩阵,B为pxn的矩阵,mxn的矩阵C为A与B的乘积,记为C=AB,其中矩阵C中的第i行第j列元素可以表示为∶
Array的创建与访问
创建array
通过列表转换为一维数组
import numpy as np
list1 = [1, 2, 3, 4]
array1 = np.array(list1)
或者
array3 = np.arange(1, 10)
print(array3)
上述函数和range()相似,都是指明始止点,如果需要指定步长可以在终止值后加上步长值
array3 = np.arange(1, 10, 2)
print(array3)
创建二维数组
import numpy as np
list1 = [1, 2, 3, 4]
list2 = [5, 6, 7, 8]
array1 = np.array([list1, list2])
print(type(array1))
print(array1)
创建全零矩阵
- 一维全零矩阵
array4 = np.zeros(5)
print(array4)
- 二维全零矩阵
array4 = np.zeros([2, 3])
print(array4)
创建单位矩阵
array5 = np.eye(5)
print(array5)
查询数组消息
- 数组的行和列
sh = array1.shape
print(sh)
代表array1是一个两行四列的数组
- 元素个数
s = array1.size
print(s)
返回的8代表array1元素个数为8
- 元素的数据类型
ty = array1.dtype
print(ty)
如果数组里面的元素数据类型不一致,选择精度最高的数据类型
array2 = np.array([[1.0, 2, 3], [4.0, 5, 6]])
print(array2.dtype)
访问数array
访问一维数组
数组的访问和列表的切片差不多
array3 = np.arange(1, 10)
print(array3[1:5])
访问二维数组
array2 = np.array([[1, 2, 3], [4, 5, 6]])
print(array2[0][2])
或者
array2 = np.array([[1, 2, 3], [4, 5, 6]])
print(array2[0, 2])
数组切片
array2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(array2[:2, 1:])
数组与矩阵的运算
随机数组的创建
- 随机生成一个长度为10的,符合正态分布的一维数组
np.random.randn(10)
- 生成一个10以内的随机数
np.random.randint(10)
- 随机生成多维数组(以2X3的数组为例)
np.random.randint(10, size=(2, 3))
- 随机生成一个一维数组
np.random.randint(10, size=20)
- 把一维数组转换为多维数组
np.random.randint(10, size=20).reshape(4, 5)
数组的运算
- 数组相加
a = np.random.randint(10, size=20).reshape(4, 5)
b = np.random.randint(10, size=20).reshape(4, 5)
print(a)
print(b)
print(a + b)
数组相加的结果是对应的每个元素分别相加
- 数组相减
a = np.random.randint(10, size=20).reshape(4, 5)
b = np.random.randint(10, size=20).reshape(4, 5)
print(a)
print(b)
print(a - b)
数组相减的结果是对应的每个元素分别相减
- 数组相乘
a = np.random.randint(10, size=20).reshape(4, 5)
b = np.random.randint(10, size=20).reshape(4, 5)
print(a)
print(b)
print(a * b)
数组相乘的结果是对应的每个元素分别相乘
- 数组相除
a = np.random.randint(10, size=20).reshape(4, 5)
b = np.random.randint(10, size=20).reshape(4, 5)
print(a)
print(b)
print(a / b)
一个注意点: 数组相除有时候会发出警报,原因是被除的数组(b)中含有0元素,当被除数为0时,结果中会自动填充一个值inf,以告诉我们该除法运算非法
矩阵的创建
矩阵的创建和数组差不多
np.mat([[1, 2, 3], [4, 5, 6]])
数组可以直接转化为一个矩阵
np.mat(a)
矩阵的运算
- 矩阵相加
a = np.random.randint(10, size=20).reshape(4, 5)
b = np.random.randint(10, size=20).reshape(4, 5)
A = np.mat(a)
B = np.mat(b)
print(A)
print(B)
print(A+B)
矩阵相加的结果是对应的每个元素分别相加
- 矩阵相减
a = np.random.randint(10, size=20).reshape(4, 5)
b = np.random.randint(10, size=20).reshape(4, 5)
A = np.mat(a)
B = np.mat(b)
print(A)
print(B)
print(A-B)
矩阵相减的结果是对应的每个元素分别相减
- 矩阵相乘
a = np.random.randint(10, size=20).reshape(4, 5)
b = np.random.randint(10, size=20).reshape(4, 5)
A = np.mat(a)
B = np.mat(b)
print(A)
print(B)
print(A*B)
注意: 直接输入A*B会报错,矩阵的乘法必须保证A矩阵的行数等于B矩阵的列数
a = np.random.randint(10, size=20).reshape(4, 5)
b = np.random.randint(10, size=20).reshape(5, 4)
A = np.mat(a)
B = np.mat(b)
print(A)
print(B)
print(A*B)
Array常用函数
- 元素去重
np.unique(a)
返回数组a中不重复的元素
- 求数组每一列的和
sum(a)
- 求数组每一行的和
sum(a[0])
- 求数组每一列的和
sum(a[:,0])
- 返回数组中的最大值
a.max()
- 返回某一行/列的最大值
max(a[0]) # 行
max(a[:, 0]) # 列
pandas入门
pandas只要同于数据分析与数据处理
pandas.Series
创建Series
pandas里面自带了一个Series类
- 利用列表创建Series
pd.Series([1, 2, 3, 4])
Series里面包含两部分,除了数据本身(右边列)外还包括了索引(左边列)
- 利用数组创建Series
pd.Series(np.arange(10))
- 利用字典创建Series
pd.Series({'1': 1, "2": 2, "3": 3})
以字典的key为索引,value为值创建Series
- 指定索引和对应的值
pd.Series([1, 2, 3, 4], index=['A', 'B', 'C', 'D'])
特殊情况,当index的数量大于值的数量时
pd.Series(s4, index=['A', 'B', 'C', 'D', 'E'])
自动填充NaN值
查看Series元素
s1.values
查看Series的索引
s1.index
索引从0开始,在4结束(结束不包含),间隔为1
访问Series的元素
- 访问某一行
s4['A']
- 访问某个范围
s4[s4 > 2]
把Series转化为字典
s4.to_dict()
空值转化
- pd.isnull()
遇见空值返回True
- pd.notnull()
不为空值返回True
Series命名
s5.name = 'Series'
s5.index.name = 'Series name'
pandas.Dataframe
创建Dataframe
一个有趣的小方法,使用webbrowser库打开链接(www.tiobe.com/tiobe-index…
link = 'https://www.tiobe.com/tiobe-index/'
webbrowser.open(link) # 打开链接
复制内容
读取粘贴板内容
df = pd.read_clipboard()
查看列名
df.columns
查看某一列的操作
df.列名
筛选某几列数据组成一个新的DataFrame
DataFrame(df, columns=['Sep2022', 'Sep2021', 'ProgrammingLanguage'])
深入理解DataFrame和Series
DataFrame和Series的关系
创建一个DataFrame,并查看某一列的数据类型
data = {'country': ['Belgium', 'Indis', 'Brazil'], 'Capital': ['Brussels', 'NewDelhi', 'Brasilia'], 'Population': [11190846, 1303171035, 207847528]}
s1 = pd.DataFrame(data)
print(type(s1['Capital']))
访问DataFrame某一行
对于DataFrame某一行,我们可以使用iterrows()函数
返回一个generator的数据类型,对于每一个generator我们都可以利用for循环进行遍历
for i in s1.iterrows():
print(i)
print(type(i))
每一行的数据都被封装为一个元组
对于DataFrame的行数据,每一行包含了两个元素,分别是index和数据
且数据部分也是由Series组成
通过拼接Series创建DataFrame
data = {'country': ['Belgium', 'Indis', 'Brazil'], 'Capital': ['Brussels', 'NewDelhi', 'Brasilia'], 'Population': [11190846, 1303171035, 207847528]}
df1 = pd.DataFrame(data)
s1 = pd.Series(data['country'])
s2 = pd.Series(data['Capital'])
s3 = pd.Series(data['Population'])
df2 = pd.DataFrame([s1, s2, s3])
对比df1
- df2不存在列名
- df2都数据颠倒了(行列颠倒)
解决办法
df2 = pd.DataFrame([s1, s2, s3], index=data.keys())
df2 = df2.T # 转置
IO操作
| FormatType | DataDescription | Reader | Writer |
|---|---|---|---|
| text | CSV | read_csv | to_csv |
| text | JSON | read_json | to_json |
| text | HTML | read_html | to_html |
| text | Local clipboard | read_clipboard | to_clipboard |
| binary | MS Excel | read_excel | to_excel |
| binary | HDF5 Format | read_hdf | to_hdf |
| binary | Feather Format | read_feather | to_feather |
| binary | Msgpack | read_msgpack | to_msgpack |
| binary | Stata | read_stata | to_stata |
| binary | SAS | read_sas | |
| binary | Python Pickle Format | read_pickle | to_pickle |
| SQL | SQL | read_sql | to_sql |
| SQL | Google Big Query | read_gbq | to_gbq |
DataFrame的Selecting和indexing
生成DataFrame
(所需要用到的文件可以在网盘中自取)
显示某几行/列数据
- 获取首尾
默认是5行
imdb.head()
imdb.tail()
- 获取中间某几行/列
sub_df = imdb[['director_name', 'movie_title', 'imdb_score']]
sub_df.iloc[10:20, :]
iloc函数中,逗号前的参数(10:20)代表着从第10行到第20行(不包括20),后面的参数是对列的限制
- 嵌套切片
对于切片获取到的DataFrame而言,再次切片时,行数仍然从0开始
- 通过index获取
tmp_df.loc[15:17, :]
该切片方法是一个闭区间获取,前后范围均包含在切片范围内
tmp_df.loc[15:17, :'movie_title']
Series和DataFrame的Reindexing
reindex的作用是对Series或DataFrame对象创建一个适应新索引的新对象。
Series reindex
s1 = pd.Series([1, 2, 3, 4], index=['A', 'B', 'C', 'D'])
s1 = s1.reindex(index=['A', 'B', 'C', 'D', 'E'])
如果不想以NaN填充,可以用fill_value方法来设置
s1 = s1.reindex(index=['A', 'B', 'C', 'D', 'E'], fill_value=5.0)
也可以用ffill方法实现前向填充,也就是补充的索引如果没有对应的数值,则取前一个索引的值填充。
ffill或pad: 前向(或进位)填充
bfill或backfill: 后向(或进位)填充
DataFrame reindex
两者区别主要在于,DataFrame可以对index或columns使用reindex方法。
- 对index
df1 = DataFrame(np.arange(9).reshape((3, 3)), index=['a', 'c', 'd'], columns=['Ohio', 'Texas', 'California'])
df2 = df1.reindex(['a', 'b', 'c', 'd'])
- 对columns
df3 = df1.reindex(columns=states)
对DataFrame使用ffill方法
df4 = df1.reindex(index=['a', 'b', 'c', 'd'], columns=states).ffill()
谈一谈NaN
NaN是"not a number"的简称,表示 Pandas 中缺少的值。
创建一个NaN
np.nan
type(n)
NaN运算
NaN in Series
检测NaN
s1 = Series([1, 2, np.nan, 3, 4], index=['A', 'B', 'C', 'D', 'E'])
- .isnull()用 True 表示具有 NaN 值的元素,用 False 表示非 NaN 值的元素
- .notnull()与.isnull()相反
删除NaN
- .dropna()用于删除NaN值
NaN in DataFrame
df1 = DataFrame([[1, 2, 3], [np.nan, 5, 6], [7, np.nan, 9], [np.nan, np.nan, np.nan]])
检测NaN
处理NaN
df = pd.DataFrame({"name": ['Alfred', 'Batman', 'Catwoman'],
"toy": [np.nan, 'Batmobile', 'Bullwhip'],
"born": [pd.NaT, pd.Timestamp("1940-04-25"),
pd.NaT]})
- 直接使用.dropna()
删除所有含有NaN的行和列
DataFrame.dropna( axis=0, how=‘any’, thresh=None, subset=None, inplace=False)
- axis参数确定是否删除包含缺失值的行或列
- axis=0或axis='index’删除含有缺失值的行,
- axis=1或axis='columns’删除含有缺失值的列,
- how参数当我们至少有一个NA时,确定是否从DataFrame中删除行或列
how='all'或者how='any'
- how='all’时表示删除全是缺失值的行(列)
- how='any’时表示删除只要含有缺失值的行(列)
- thresh=n表示保留至少含有n个非na数值的行
- subset定义要在哪些列中查找缺失值
df.dropna(subset=['name', 'born'])
- inplace表示直接在原DataFrame修改
多级的index
在pandas中可以为series和dataframe设置多个index,也就是说可以有多级index和column。这样可以对pandas的操作更加灵活。
- Series 多级index
- 创建多级的index
s1 = Series(np.random.randn(6), index=[['1', '1', '1', '2', '2', '2'], ['a', 'b', 'c', 'a', 'b', 'c']])
- 获取 index 为 1 的 series
s1['1']
- 获取 column 为 a 的 series
s1[:, 'a']
- 多级的Series, 可以转化为一个 dataframe
- 二级series 可以转化dataframe
s1.unstack()
- dataframe 转化为 series
s2 = df1.unstack()
s2 = df1.T.unstack()
- 创建 一个 多级的 dataframe
df = DataFrame(np.arange(16).reshape(4, 4), index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]], \
columns=[['beijing', 'beijing', 'shanghai', 'shanghai'], [8, 9, 8, 9]]
)
- 访问 多级 dataframe 的元素
Mapping和Replace
- mapping
map()函数可以用于Series对象或DataFrame对象的一列,接收函数作为或字典对象作为参数,返回经过函数或字典映射处理后的值。
- 创建一个DataFrame
df1 = DataFrame({"城市":["北京","上海","广州"], "人口":[1000,2000,1500]}, index=['A','B','C'])
- 附加多一列数据'GDP'
df1['GDP'] = Series([1000, 2000, 1500], index=['A', 'B', 'C'])
- map()函数使用
df1['GDP'] = df1['GDP'].map(lambda x: x + 1)
- replace
-
生成0到9的Series
s1 = Series(np.arange(10))
- 使用.replace()后并不会修改源数据
s1.replace([1, 2, 3], [10, 20, 30])
pandas玩转数据
Series和DataFrame的简单数学运算
Series运算
对于series而言,对于index相同的值,会自动对齐相加,对于未重叠的部分,会将他们展示并用NAN值填充(类似于数据库当中的外连接所不同的是用NAN值填充了)
s1 = Series([1, 2, 3], index=['A', 'B', 'C'])
s2 = Series([4, 5, 6, 7], index=['B', 'C', 'D', 'E'])
print(s1 + s2)
NaN加任何数都为NaN
DataFrame的运算
对于dataframe而言结果也是一致的,只是它的对齐对象需要是index和column都相同的部分,未重叠的部分会以NAN值填充。
df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'), index=['Ohio', 'Texas', 'Colorado'])
df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])
print(df1 + df2)
- df.add函数
以NAN值填充那些不重叠的部分可能不是我们想要的,我们想以两个DF当中原本的值去填充,这就更像是外连接了,这时就可以使用df.add,单纯的add是行不通的,需要参数fill_value=0,参数fill_value=0是将两者不重叠的部分用0填充,所以最后得到的结果就是,原本不重叠的部分以NAN值填充的部分以0进行填充,相加的结果是原本的df的值。
df1.add(df2)
df1.add(df2, fill_value=0)
- 除了加法以外,还有以下算术方法,以r开头的函数没有什么不同只是会反转参数。
| 方法 | 说明 |
|---|---|
| add,radd | 用于加法(+)的方法 |
| sub,rsub | 用于减法(-)的方法 |
| div,rdiv | 用于除法(/) 的方法 |
| floordiv,rfloordiv | 用于底除(//) 的方法 |
| mul,rmul | 用于乘法(*)的方法 |
| pow,rpow | 用于指数(**)的方法 |
dataframe和series之间的运算
frame = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])
series = frame.iloc[0]
print(frame - series)
- 如同上面的dataframe之间的运算当计算时,发现有未重叠的部分的那么未重叠的部分将以NAN值填充,并显示。
series2 = pd.Series(range(3), index=['b', 'e', 'f'])
- 假如要按列进行广播,则需要用算术函数并且传入轴
当我们从arr当中减去arr[0],每一行都会这样做,这种从上到下都会执行相同操作的方式叫做广播。
series3 = frame['b']
frame.add(series3,axis=0)
函数应用和映射
Numpy的元素级应用方法也可以应用于pandas对象。
frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'),index=['Utah', 'Ohio', 'Texas', 'Oregon'])
print(np.abs(frame))
pandas本身也有将函数应用于pandas对象的方法,applymap,apply,map,不同的是applymap是只能整个数组,apply是用于一个series或者整个dataframe,map是只能用于series级别
frame['b'].apply(lambda x: abs(x))
frame['b'].map(lambda x: abs(x))
frame.applymap(lambda x: abs(x))
apply还可以用在整个frame
frame.apply(lambda x: abs(x))
因此在实际使用过程当中推荐使用apply就可以了,因为他既可以用在frame也可以用在series当中。
Series和DataFrame排序
Series排序
- sort_values()
s1 = Series(np.random.randn(10))
s2 = s1.sort_values()
参数说明:
-
by:指定列名(axis=0或’index’)或索引值(axis=1或’columns’)
-
axis:若axis=0或’index’,则按照指定列中数据大小排序;若axis=1或’columns’,则按照指定索引中数据大小排序,默认axis=0
-
ascending:是否按指定列的数组升序排列,默认为True,即升序排列
-
inplace:是否用排序后的数据集替换原来的数据,默认为False,即不替换
-
na_position:{‘first’,‘last’},设定缺失值的显示位置
- .sort_index()
s1 = Series(np.random.randn(10))
s2 = s1.sort_index()
参数介绍:
- axis:轴索引(排序的方向),0表示按index,1表示按columns
- level:若不为None,则对指定索引级别的值进行排序
- ascending:是否升序排列,默认为True,表示升序
- inplace:默认为False,表示对数据表进行排序,不创建新的实例
- kind:选择排序算法
DataFrame排序
对于DataFrame而言,直接使用.sort_values()函数会报错,需要传入排序的列
重命名DataFrame的index
- 通过修改Series重命名
df1 = DataFrame(np.arange(9).reshape(3, 3), index=['BJ', 'SH', 'GZ'], columns=['A', 'B', 'C'])
df1.index = Series(['bj', 'sh', 'gz'])
- map函数
df1 = DataFrame(np.arange(9).reshape(3, 3), index=['bj', 'sh', 'gz'], columns=['A', 'B', 'C'])
df1.index.map(str.upper)
str.upper:把字符串中的字母转化为大写
str.lower:把字符串中的字母转化为小写
map函数是生成一个Index对象,而不是永久改变df1的值
想要永久改变df1的Index值,需要同时运用方法1和2
df1.index = df1.index.map(str.upper)
- rename函数
df1.rename(index=str.lower)
该函数用于产生一个修改后的DataFrame对象,不是直接修改df
- 通过字典进行个别行/列修改
df1.rename(index={'BJ': 'beijing'}, columns={'A': 'a'})
DataFrame的merge操作
merge操作类似于数据库当中两张表的join,可以通过一个或者多个key将多个dataframe链接起来。
df1 = pd.DataFrame({'id': [1, 2, 3, 3, 5, 7, 6], 'age': range(7)})
df2 = pd.DataFrame({'id': [1, 2, 4, 4, 5, 6, 7], 'score': range(7)})
- 通过id对两个DataFrame进行连接
pd.merge(df1, df2)
没有指定根据哪一列完成关联时,pandas会自动寻找两个DataFrame的名称相同列来进行关联。如果遇见需要指定列名的情况,只需要传入on这个参数即可。
pd.merge(df1, df2, on='id')
根据多列关联,也可以传入一个数组
假如两个DataFrame当中的列名不一致,我们需要用left_on指定左表用来join的列名,用right_on指定右表用来join的列名
df1 = pd.DataFrame({'id': [1, 2, 3, 3, 5, 7, 6], 'age': range(7)})
df2 = pd.DataFrame({'number': [1, 2, 4, 4, 5, 6, 7], 'score': range(7)})
pd.merge(df1, df2, left_on='id', right_on='number')
问题:
观察一下上面的结果会发现关联之后的数据条数变少了
原因:
默认的方式是inner join,也就是两张表当中都存在的数据才会被保留。如果是left join,那边左边当中所有的数据都会保留,关联不上的列置为None,同理,如果是right join,则右表全部保留,outer join则会全部保留(可联想MySQL中相关的操作)
- 左表保留
pd.merge(df1, df2, left_on='id', right_on='number', how='left')
- 其它参数介绍
- left: 拼接的左侧DataFrame对象
- right: 拼接的右侧DataFrame对象
- on: 要加入的列或索引级别名称。 必须在左侧和右侧DataFrame对象中找到。 如果未传递且left_index和right_index为False,则DataFrame中的列的交集将被推断为连接键。
- left_on:左侧DataFrame中的列或索引级别用作键。 可以是列名,索引级名称,也可以是长度等于DataFrame长度的数组。
- right_on: 左侧DataFrame中的列或索引级别用作键。 可以是列名,索引级名称,也可以是长度等于DataFrame长度的数组。
- left_index: 如果为True,则使用左侧DataFrame中的索引(行标签)作为其连接键。 对于具有MultiIndex(分层)的DataFrame,级别数必须与右侧DataFrame中的连接键数相匹配。
- right_index: 与left_index功能相似。
- how: One of ‘left’, ‘right’, ‘outer’, ‘inner’. 默认inner。inner是取交集,outer取并集。比如left:[‘A’,‘B’,‘C’];right[’'A,‘C’,‘D’];inner取交集的话,left中出现的A会和right中出现的买一个A进行匹配拼接,如果没有是B,在right中没有匹配到,则会丢失。'outer’取并集,出现的A会进行一一匹配,没有同时出现的会将缺失的部分添加缺失值。
- sort: 按字典顺序通过连接键对结果DataFrame进行排序。 默认为True,设置为False将在很多情况下显着提高性能。
- suffixes: 用于重叠列的字符串后缀元组。 默认为(‘x’,’ y’)。
- copy: 始终从传递的DataFrame对象复制数据(默认为True),即使不需要重建索引也是如此。
- indicator:将一列添加到名为_merge的输出DataFrame,其中包含有关每行源的信息。 _merge是分类类型,并且对于其合并键仅出现在“左”DataFrame中的观察值,取得值为left_only,对于其合并键仅出现在“右”DataFrame中的观察值为right_only,并且如果在两者中都找到观察点的合并键,则为left_only。
Concatenate和Combine
矩阵中的中的Concatenate和Combine
创建两个33的矩阵(arr1与arr2)并把两个矩阵连接起来,生成一个36的矩阵
arr1 = np.arange(9).reshape(3, 3)
arr2 = np.arange(9).reshape(3, 3)
np.concatenate([arr1, arr2])
横着连接,把arr2的数据放到arr1后面
np.concatenate([arr1, arr2], axis=1)
Series 中的Concatenate和Combine
Series 中的Concatenate
创建两个Series(s1和s2),连接两个Series,直接把s2拼接到s1下方
s1 = Series([1, 2, 3], index=['X', 'Y', 'Z'])
s2 = Series([4, 5], index=['A', 'B'])
pd.concat([s1, s2])
左右连接两个Series
pd.concat([s1, s2], axis=1)
Series中的Combine
s1 = Series([2, np.nan, 4, np.nan], index=['A', 'B', 'C', 'D'])
s2 = Series([1, 2, 3, 4], index=['A', 'B', 'C', 'D'])
s1.combine_first(s2)
combine_first会用s2里面的values填充s1里面没有的值
DataFrame中的Concatenate和Combine
DataFrame中的Concatenate
df1 = DataFrame(np.random.randn(4, 3), columns=['X', 'Y', 'Z'])
df2 = DataFrame(np.random.randn(3, 3), columns=['X', 'Y', 'A'])
pd.concat([df1, df2])
concat后会连接在一起,把没有的值使用NaN进行填充
DataFrame中的Combine
df1 = DataFrame({'X': [1, np.nan, 3, np.nan], 'Y': [5, np.nan, 7, np.nan], 'Z': [9, np.nan, 11, np.nan]})
df2 = DataFrame({'Z': [np.nan, 10, np.nan, 12], 'A': [1, 2, 3, 4], })
df1.combine_first(df2)
DataFrame的combine_first的结果和Series的结果是一样的,A这一列没变化,X和Y并没有被填充因为df2里面没有,Z这一列为两个DataFrame相互填充的结果(df1的‘Z’列偶数行为NaN,df2的‘Z’列奇数行为NaN)
通过apply进行数据预处理
(所需要用到的文件可以在网盘中自取)
- 读取数据
df = pd.read_csv()
- 添加列数据
s1 = Series(['a']*7978)
df['A'] = s1
- 使用apply对数据进行处理
- 把字母变成大写
df['A'] = df['A'].apply(str.upper)
- 去掉头尾的空格,以中间的空格分隔
df['data'][0].strip().split(' ')
- 用','分隔
df['data'][0].strip().split(',')
- 通过apply使用函数分隔
df = pd.read_csv()
def foo(line):
items = line.strip().split(' ')
return Series([items[1], items[3], items[5]])
df_tmp = df['data'].apply(foo)
df_tmp = df_tmp.rename(columns={0: "Symbol", 1: "Seqno", 2: "Price"})
- 加入df_new中没有,df_tmp存在的属性列
df_new = df.combine_first(df_tmp)
- 删除df_new中的'data'属性列
del df_new['data']
通过去重进行数据清洗
(所需要用到的文件可以在网盘中自取)
- 添加数据
- 删除无用数据
del df['Unnamed: 0']
- 查看非重复的数据
df['Seqno'].unique()
返回一个不含重复值列表
- 检测是否与前边重复
df['Seqno'].duplicated()
- 删除重复数据
df['Seqno'].drop_duplicates()
- 其它参数展示
-
subset: 列名,可选,默认为None
-
keep: {‘first’, ‘last’, False}, 默认值 ‘first’
- first: 保留第一次出现的重复行,删除后面的重复行。
- last: 删除重复项,除了最后一次出现。
- False: 删除所有重复项。
-
inplace:布尔值,默认为False,是否直接在原数据上删除重复项或删除重复项后返回副本。(inplace=True表示直接在原来的DataFrame上删除重复项,而默认值False表示生成一个副本。)
时间序列操作基础
- 创建时间列表
date_list = [datetime(2022, 1, 1), datetime(2022, 1, 2), datetime(2022, 1, 3), datetime(2022, 1, 4),datetime(2022, 1, 5)]
- 创建DataFrame
s1 = Series(np.random.rand(5), index=date_list)
- 利用时间序列筛选数据
s1[datetime(2022, 1, 2)]
利用datetime的其它格式筛选也能达到一样的目的
s1['2022-1-2']
s1['20220102']
根据年/月份筛选
date_list = [datetime(2021, 1, 1), datetime(2021, 2, 2), datetime(2022, 1, 3), datetime(2022, 2, 4), datetime(2022, 1, 5)]
s1 = Series(np.random.rand(5), index=date_list)
s1['2022-01']
s1['2022']
- date_range函数
参数:
- periods:固定时期,取值为整数或None
- freq:日期偏移量,取值为string或DateOffset,默认为'D'
- normalize:若参数为True表示将start、end参数值正则化到午夜时间戳
- name:生成时间索引对象的名称,取值为string或None
- closed:可以理解成在closed=None情况下返回的结果中,若closed=‘left’表示在返回的结果基础上,再取左开右闭的结果,若closed='right'表示在返回的结果基础上,再取做闭右开的结果
pd.date_range(start='2022-01-01', periods=30)
时间序列数据的采样
- 生成时间序列
t_range = pd.date_range(start='2022-01-01', end='2022-12-31')
- 创建Series
s1 = Series(np.random.randn(len(t_range)), index=t_range)
- 传统采样和采样方法的改进
- 传统采样方法(求月数据的平均值)
s1['2022-06'].mean()
传统采样一次只能获取到某一个月的信息
- 改进---resample函数
DataFrame.resample(rule, axis=0, closed=None, label=None, convention='start', kind=None, loffset=None, base=None, on=None, level=None, origin='start_day', offset=None) [source]
参数说明:
-
rule : eDateOffset, Timedelta 或 str表示目标转换的偏移字符串或对象。
-
axis :{0 或‘index’, 1 或 ‘columns’}, 默认为 0向上采样或向下采样使用哪一个轴。对于级数,默认值为0,即沿着行。必须是DatetimeIndex, TimedeltaIndex或PeriodIndex。
-
closed : {‘right’, ‘left’}, 默认为Nonebin区间的哪一边是关闭的。除了‘M’、‘A’、‘Q’、‘BM’、‘BA’、‘BQ’和‘W’之外,所有频率偏移的默认值都是‘left’,它们的默认值都是‘right’。
-
label : {‘right’, ‘left’}, 默认为 None用哪边标签来标记bucket。除了‘M’、‘A’、‘Q’、‘BM’、‘BA’、‘BQ’和‘W’之外,所有频率偏移的默认值都是‘left’,它们的默认值都是‘right’。
-
convention : {'start', 'end', 's', 'e'}, 默认为 'start'仅对于PeriodIndex,控制是使用规则的开始还是结束。
-
kind : {‘timestamp’, ‘period’}, 可选, 默认为 None传递'timestamp'将结果索引转换为DateTimeIndex,或'period'将其转换为PeriodIndex。默认情况下,保留输入表示形式。
-
loffset : timedelta, 默认为None调整重新采样的时间标签。自1.1.0版本以来已弃用 : 您应该将loffset添加到df中。重新取样后的索引。见下文。
-
base : int, 默认为0,对于平均细分1天的频率,聚合间隔的“origin”。例如,对于“5min”频率,基数可以从0到4。默认值为0。自1.1.0版本以来已弃用:您应该使用的新参数是'offset'或'origin'。
-
on : str, 可选自1.1.0版本以来已弃用:您应该使用的新参数是'offset'或'origin'。
-
level : str 或 int, 可选用于重采样的多索引、级别(名称或数字)。级别必须与日期时间类似。
-
origin : {‘epoch’, ‘start’, ‘start_day’}, Timestamp 或 str, 默认为 ‘start_day’
-
调整分组的时间戳。原始时区必须与索引的时区匹配。
-
如果不使用时间戳,也支持以下值:
-
- “epoch”:起源是1970年01月01日
-
- 'start ': origin是timeseries的第一个值
-
- “start_day”:起源是timeseries午夜的第一天
-
新版本1.1.0。
-
offset : Timedelta 或 str, 默认为 None加到原点的偏移时间。
- 对月数据求平均
s1.resample('M').mean()
- 将数据往前填充(按小时填充)
s1.resample('H').ffill()
- 往后填充
s1.resample('H').bfill()
数据分箱技术
对成绩数据进行分箱
score_list = np.random.randint(25, 100, size=20) # 随机生成20个在区间25~100中的数值
bins = [0, 59, 70, 80, 100] # 设置分箱等级
pd.cut(score_list, bins=bins)
-
pd.cut函数
-
pd.cut( x, bins, right=True, labels=None, retbins=False, precision=3, include_lowest=False, duplicates='raise', ) -
x : 一维数组
-
bins :整数,标量序列或者间隔索引,是进行分组的依据,
-
如果填入整数n,则表示将x中的数值分成等宽的n份(即每一组内的最大值与最小值之差约相等);
- 如果是标量序列,序列中的数值表示用来分档的分界值
- 如果是间隔索引,“ bins”的间隔索引必须不重叠
-
right :布尔值,默认为True表示包含最右侧的数值
- 当“ right = True”(默认值)时,则“ bins”=[1、2、3、4]表示(1,2],(2,3],(3,4]
- 当bins是一个间隔索引时,该参数被忽略。
-
labels : 数组或布尔值,可选.指定分箱的标签
- 如果是数组,长度要与分箱个数一致,比如“ bins”=[1、2、3、4]表示(1,2],(2,3],(3,4]一共3个区间,则labels的长度也就是标签的个数也要是3
- 如果为False,则仅返回分箱的整数指示符,即x中的数据在第几个箱子里
- 当bins是间隔索引时,将忽略此参数
-
retbins: 是否显示分箱的分界值。默认为False,当bins取整数时可以设置retbins=True以显示分界值,得到划分后的区间
-
precision:整数,默认3,存储和显示分箱标签的精度。
-
include_lowest:布尔值,表示区间的左边是开还是闭,默认为false,也就是不包含区间左边。
-
duplicates:如果分箱临界值不唯一,则引发ValueError或丢弃非唯一
-
对分箱结果进行统计
pd.value_counts()
创建学生成绩DataFrame
df = DataFrame()
df['score'] = score_list
df['student'] = [pd.util.testing.rands(3) for i in range(20)] # 随机生成长度为3的字符串
把分箱结果插入DataFrame中
df['categories'] = pd.cut(df['score'], bins)
格式化分箱结果
df['categories'] = pd.cut(df['score'], bins, labels=['Low', 'OK', 'Good', 'Greate'])
数据分组技术GroupBy
该分组技术与MySQL的分组类似
(所需要用到的文件可以在网盘中自取)
DataFrame.groupby(by=None, axis=0,level=None,as_index=True, sort=True, group_keys=True, squeeze=NoDefault.no_default, observed=False, dropna=True)
- by:用于确定 groupby 的组。 如果 by 是一个函数,它会在对象索引的每个值上调用。 如果传递了 dict 或 Series,则 Series 或 dict VALUES 将用于确定组(Series 的值首先对齐;参见 .align() 方法)。 如果传递了长度等于所选轴的列表或 ndarray,则按原样使用这些值来确定组。 一个标签或标签列表可以通过 self 中的列传递给 group。 请注意,元组被解释为(单个)键。
- axis:沿行 (0) 或列 (1) 拆分。
- level:如果轴是MultiIndex(层次化),则按一个或多个特定级别进行分组。
- as_index:对于聚合输出,返回具有组标签作为索引的对象。仅与DataFrame输入相关。as index=False是有效的sql风格的分组输出。
- sort:对组键进行排序。 关闭此功能可获得更好的性能。 请注意,这不会影响每组内的观察顺序。 Groupby 保留每个组内的行顺序。
- group_keys:当调用apply时,将组键添加到index以识别片段。
- squeeze:如果可能,降低返回类型的维数,否则返回一致的类型。
- observed:这仅适用于任何 groupers 是分类的。 如果为真:仅显示分类分组的观察值。 如果为 False:显示分类分组的所有值。
- dropna:如果为 True,并且组键包含 NA 值,则 NA 值连同行/列将被删除。 如果为 False,NA 值也将被视为组中的键。
- 通过城市进行分组
df.groupby(df['city'])
返回一个DataFrameGroupBy对象
- 查看分组结果
.groups
返回一个字典类型的数据,字典的键key是分组的值,字典的值value是该组里面每一个元素的索引
- 查看某一分组数据
g.get_group
返回一个DataFrame
- 查看每一个分组的数据情况
g.方法名
- 分组原理
- 把分组结果转化为list
list(g)
数据聚合技术
普通聚合
g.describe()
分组运算方法
pandas提供了多种分组运算方法,包括aggregate、apply和transform,它们的用法各有不同,适用于不同的场景
- aggregate方法
前面用到的聚合函数都是直接在DataFrameGroupBy上调用的,这样分组以后所有列做的都是同一种汇总运算,且一次只能使用一种汇总方式。
aggregate的神奇之处在于,一次可以使用多种汇总方式,比如下面的例子先对分组后的所有列做计数汇总运算,然后对所有列做求和汇总运算。
aggregate方法是一个既能作用于Series、DataFrame,也能作用于GroupBy的聚合方法。aggregate方法接收函数并应用于每个分组,返回标量值,其基本语法格式如下
Groupby.aggregate(func,axis=0,*args,**kwargs)
-
func : 指定用于集合运算的函数,具体类型包含自定义函数名、字符串函数名、列表函数名,字典函数名。该参数支持的统计函数是pandas、numpy、scipy、python提供的所有统计函数,也可以是自定义函数
-
axis :值为0则在列向做聚合运算,值为1则在行向做聚合运算
- 内置函数聚合
-
df.groupby(df['city']).aggregate("count") - 自定义函数聚合
-
def Max_cut_Min(group): return group.max()-group.min() df = pd.read_csv(r'D:\study\ana_data\city_weather.csv') g = df.groupby(df['city']).aggregate(Max_cut_Min) -
df.groupby("city").aggregate(lambda group:group.max()-group.min()) # 运用lambda表达式 -
上下两个代码可以实现同样的效果
- 单列聚合
-
g = df.groupby(df['city'])['wind'].aggregate(np.mean) - 多列聚合
-
df.groupby(df['city']).aggregate(np.mean) - 多方式聚合
-
df.groupby(df['city'])['wind'].aggregate([np.mean, np.min]) - 多种聚合运算的同时更改列名
-
df.groupby(df['city'])['wind'].aggregate([('平均值', np.mean), ('最小值', np.min)]) - 不同的列运用不同的聚合函数
df.groupby(df['city']).aggregate({'wind': [('平均值', np.mean), ('最小值', np.min)], 'temperature': [('平均值', np.mean), ('最大值', np.max)]})
-
apply
-
transform
-
filter
透视表
透视表是一种可以对数据动态排布并且分类汇总的表格格式
(所需要用到的文件可以在网盘中自取)
字段解释:
- Account:销售的账户
- Name:客户名字
- Rep:销售员
- Manager:销售员上司
- Product:卖出去的产品
- Quantity:数量
- Price:单价
- Status:订单状态
生成透视表
pd.pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')
几个重要的参数:
- data:DataFrame对象
- values:源数据中的一列,数据透视表中用于观察分析的数据值,类似Excel中的值字段
- index:源数据中的一列,数据透视表用于行索引的数据值,类似Excel中的行字段
- columns:源数据中的一列,数据透视表用于列索引的数据值,类似Excel中列字段
- aggfunc:根据当前的行、列索引生成的数据透视表中有多个数据需要进行聚合时,对这多个数据需要进行的操作,默认为np.mean()
通过Name字段统计
pd.pivot_table(df, index=['Name'], aggfunc='sum')
多字段透视
pd.pivot_table(df, index=['Name', 'Rep', 'Manager'])
对比
可以看出,Rep和Manager字段的先后顺序影响着透视结果,这是因为,Rep和Manager之间存在着一个多对一的关系
为了避免上述错误的产生,提前了解需要分析的数据结构很重要
单独透视某一列数据
pd.pivot_table(df, index=['Manager', 'Rep'], values=['Price'])
分组和透视功能实现
projects.fivethirtyeight.com/flights/
数据来源
(所需要用到的文件可以在网盘中自取)
字段解释:
- flight_date:飞行日期
- unique_carrier:航空公司号
- flight_num:飞行号码
- origin:机场城市
- dest:机场名称
- arr_delay:延迟时间
- cancelled:是否延迟
- distance:距离
- ..._delay:延迟原因
- actual_elapsed_time:飞行时间
通过延误时间排序
df.sort_values('arr_delay')[:20]
显示全部的行列数据
# 显示Dateframe所有列(参数设置为None代表显示所有行,也可以自行设置数字)
pd.set_option('display.max_columns', None)
# 显示Dateframe所有行
pd.set_option('display.max_rows', None)
# 设置Dataframe数据的显示长度,默认为50
pd.set_option('max_colwidth', 200)
# 禁止Dateframe自动换行(设置为Flase不自动换行,True反之)
pd.set_option('expand_frame_repr', False)
降序排序
df.sort_values('arr_delay', ascending=False)[:20]
计算航班延误与不延误的比例
df['cancelled'].value_counts()
判断航班是否延误
df['delayed'] = df['arr_delay'].apply(lambda x: x > 0)
求延误比例
delay_data[1]/(delay_data[0]+delay_data[1])
求各航空公司的延误比
- 分组
delay_group = df.groupby(['unique_carrier', 'delayed'])
- 求比
delay_group.size()
delay_group.size().unstack()
绘图和可视化之Matplotlib
官网地址:matplotlib.org/
Matplotlib介绍
实现的功能与matlab绘图功能相似
为什么要用python画图?
- GUI太复杂(matlab绘图基于GUI)
- excel功能不足
- python使用简单且免费(matlab收费)
什么是Matplotlib?
- 一个python包
- 主要用于绘制2D图像,也可以绘制3D
- 功能强大且流行(主要体现在深度学习中的图形化页面)
- 拓展多(包括Seaborn)
Matplotlib的架构
- Backend:主要处理把图显示到哪里和画到哪里
- Artist:图像显示成什么样?
- Scripting(脚本): pyplot ,Python语法和API
matplotlib的简单绘图---plot
简单的绘图
#单条线:
plot([x], y, [fmt], data=None, **kwargs)
#多条线一起画
plot([x], y, [fmt], [x2], y2, [fmt2], ..., **kwargs)
参数[fmt] 是一个字符串来定义图的基本属性如:颜色(color),点型(marker),线型(linestyle)
具体形式 fmt = '[color][marker][line]'
a = [1, 2, 3]
plt.plot(a)
plt.show()
多维度绘图
a = [1, 2, 3]
b = [4, 5, 6]
plt.plot(a, b, 'b*-')
plt.show()
注意: a和b的长度不一致会报错
绘制多个图像
a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]
d = [10, 11, 12]
plt.plot(a, b, 'b*-', c, d, 'ro--')
plt.show()
绘制sin图像
t = np.arange(0.0, 2.0, 0.1)
s = np.sin(t*np.pi)
plt.plot(t, s)
plt.show()
设置label
t = np.arange(0.0, 2.0, 0.1)
s = np.sin(t*np.pi)
plt.plot(t, s)
plt.xlabel('x')
plt.ylabel('y=sinx')
plt.title('绘制sin图像')
plt.show()
直接运行显示中文标题系统会发出警报,且图像显示不出中文部分
解决办法
指定显示字体
t = np.arange(0.0, 2.0, 0.1)
s = np.sin(t*np.pi)
plt.plot(t, s)
plt.xlabel('x')
plt.ylabel('y=sinx')
plt.title('绘制sin图像', fontproperties="SimHei")
plt.show()
t = np.arange(0.0, 2.0, 0.1)
s = np.sin(t*np.pi)
plt.plot(t, s, 'b--', label='sinx')
plt.plot(t*2, s, 'r--', label='sin2x')
plt.xlabel('x')
plt.ylabel('y=sinx')
plt.title('绘制sin图像', fontproperties="SimHei")
plt.legend()
plt.show()
matplotlib的简单绘图---subplot
plt.subplot(nrows, ncols, index, **kwargs)
前三个参数分别为行数、列数、索引数
在划分子图时,并不像传统的表格一样,先划分好,然后表示要占用几个表格。而是在绘制子图的时候,告知如何划分,然后基于这种划分,在第几块进行绘制
- 子图1:(2,2,1) ,将总图切分成两行两列,在第1个方框中绘制图像。
- 子图2:(2,2,2),将总图切分成两行两列,在第2个方框中绘制图像
- 子图3:(2,1,2),将总图切分成两行一列,在这样的切分下前提下,在第2个方框中绘制图像。
子图绘制
a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]
plt.subplot(2, 1, 1)
plt.plot(a, b, 'b')
plt.ylabel("b")
plt.subplot(2, 1, 2)
plt.plot(a, c, 'r')
plt.ylabel('c')
plt.xlabel('a')
plt.show()
a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]
d = [10, 11, 12]
plt.subplot(2, 2, 1)
plt.plot(a, b, 'b')
plt.ylabel("b")
plt.subplot(2, 2, 2)
plt.plot(a, c, 'r')
plt.ylabel('c')
plt.xlabel('a')
plt.subplot(2, 2, 3)
plt.plot(a, d, 'y--')
plt.show()
a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]
d = [10, 11, 12]
plt.subplot(221)
plt.plot(a, b, 'b')
plt.ylabel("b")
plt.subplot(222)
plt.plot(a, c, 'r')
plt.ylabel('c')
plt.xlabel('a')
plt.subplot(223)
plt.plot(a, d, 'y--')
plt.subplot(224)
plt.plot(a, d, 'b*-')
plt.show()
subplots函数
subplots(nrows=1, ncols=1, sharex=False, sharey=False, squeeze=True,subplot_kw=None, gridspec_kw=None, **fig_kw)
参数说明
-
nrows,ncols:子图的行列数。
-
sharex, sharey:
- 设置为 True 或者 ‘all’ 时,所有子图共享 x 轴或者 y 轴,
- 设置为 False or ‘none’ 时,所有子图的 x,y 轴均为独立,
- 设置为 ‘row’ 时,每一行的子图会共享 x 或者 y 轴,
- 设置为 ‘col’ 时,每一列的子图会共享 x 或者 y 轴。
-
squeeze:
- 默认为 True,是设置返回的子图对象的数组格式。
- 当为 False 时,不论返回的子图是只有一个还是只有一行,都会用二维数组格式返回他的对象。
- 当为 True 时,如果设置的子图是(nrows=ncols=1),即子图只有一个,则返回的子图对象是一个标量的形式,如果子图有(N×1)或者(1×N)个,则返回的子图对象是一个一维数组的格式,如果是(N×M)则是返回二位格式。
-
subplot_kw:字典格式,传递给 add_subplot() ,用于创建子图。
-
gridspec_kw:字典格式,传递给 GridSpec 的构造函数,用于创建子图所摆放的网格
返回值
fig: matplotlib.figure.Figure 对象
ax:子图对象( matplotlib.axes.Axes)或者是他的数组
- 生成画布
figure, ax = plt.subplots( )
- 简单绘图
a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]
d = [10, 11, 12]
figure, ax = plt.subplots(2, 2)
ax[0][0].plot(a, b, 'r--')
ax[0][1].plot(c, d, 'b--')
plt.show()