数据分析
1. 什么是数据分析 :
数据分析:是把隐藏在一些看似杂乱无章的数据背后的信息提炼出来,总结出所研究对象的内在规律
数据分析三剑客:
Numpy,Pandas,Matplotlib
2. Numpy
NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。
2.1 创建ndarray
使用np.array()创建
import numpy as np
# 一维
arr = np.array([1,2,3,4,5])
print(arr,type(arr)) # [1 2 3 4 5] <class 'numpy.ndarray'> ---来自pycharm
# 二维 --来自jupyter notebook
arr = np.array([[1,2,3],[4,5,6]])
arr
# array([[1, 2, 3],
# [4, 5, 6]])
注意 :
arr = np.array([1,'two',3])
arr
# array(['1', 'two', '3'], dtype='<U11')
arr = np.array([['1','two','3'],[1,2,3]])
arr
# array([['1', 'two', '3'],
# ['1', '2', '3']], dtype='<U3')
numpy默认ndarray的所有元素的类型是统一的
若传进来的列表包含不同的类型的元素,则会同意为一个类型,优先级 : str > float > int
2.2 使用matplotlib.pyplot获取一个numpy数组,数据来源于一张图片
利用matplotlib读取图片数据到数组中 :
import matplotlib.pyplot as plt
img_arr = plt.imread('./落日.jpg') # 读取图片,返回的是numpy数组
img_arr
#
array([[[224, 88, 14], # 三维数组
[224, 88, 14],
[224, 88, 14],
... ... ... ...
[ 6, 2, 3]]], dtype=uint8)
plt.imshow(img_arr) # 将读取的三维数组的数据以图片的形式展示出来
plt.imshow(img_arr-100) # 图片变了
2.3 ndarray的属性
- dtype:数组元素的数据类型
- size:数组元素的个数
- ndim:数组的维数
- shape:数组的维度大小(以元组形式)
import matplotlib.pyplot as plt
img_arr = plt.imread('./落日.jpg')
img_arr.ndim # 3 获取数组的维度
img_arr.shape # (332, 500, 3) 获取数组的外形
img_arr.size # 498000 获取数组的大小
img_arr.dtype # dtype('uint8') 获取数组的数据类型
2.4 np的routines函数创建
1. np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None) 等差数列数组
np.linspace(1,100,num=20) # 产生1-100之间数量20个的等差数组
# 结果:
array([ 0. , 5.26315789, 10.52631579, 15.78947368,
21.05263158, 26.31578947, 31.57894737, 36.84210526,
42.10526316, 47.36842105, 52.63157895, 57.89473684,
63.15789474, 68.42105263, 73.68421053, 78.94736842,
84.21052632, 89.47368421, 94.73684211, 100. ])
2. np.arange([start, ]stop, [step, ]dtype=None) 等差步长数组
np.arange(0,100,step=2) # 产生0-100,步长为2的等差数组
3. random.randint(low,high=None,size=None,dtype="1")
np.random.randint(0,100) # 生成0-100 之间的一个随机数
np.random.randint(0,100,size=(4,5)) # 生成 0-100 之间的随机数组,size为数组大小
4. 时间种子/随机因子
时间种子(随机因子):无时无刻都在变化的值(系统时间)
固定随机因子就可以固定随机函数的随机性
np.random.seed(90) # 时间因子90是任意数值
np.random.randint(0,100,size=(3,5))
5. np.random.random(size=None)
np.random.seed(3) # 生成0到1的随机数,左闭右开
2.5 ndarray的基本操作
-
索引
arr = np.random.randint(0,100,size=(5,6)) arr array([[28, 74, 88, 9, 15, 18], [80, 71, 88, 11, 17, 46], [ 7, 75, 28, 33, 84, 96], [88, 44, 5, 4, 71, 88], [88, 50, 54, 34, 15, 77]]) arr[0] # array([28, 74, 88, 9, 15, 18]) 第一行 arr[0,0] # 28 第一行的第一列 arr[0,1] # 74 第一行的第二列 arr[0,[2,3]] # array([88, 9]) 第一行的第三四列
-
切片
- 切取前两行
arr[0:2] # 切取前两行 # 结果 array([[28, 74, 88, 9, 15, 18], [80, 71, 88, 11, 17, 46]])
- 切取前两列
arr[:,0:2] # 切取前两列 # 结果 array([[28, 74], [80, 71], [ 7, 75], [88, 44], [88, 50]])
- 切取前两行的前两列
arr[0:2:,0:2] # 切取前两行的前两列 # 结果 array([[28, 74], [80, 71]])
-
行倒序/列倒序
arr[::-1] # 行倒序 arr[:,::-1] # 列倒序
-
全部倒序
arr[::-1,::-1] # 全部倒序
-
2.6 图片翻转
让图片数组 列 倒序 - 图片水平翻转180°
import matplotlib.pyplot as plt
img_arr = plt.imread('./落日.jpg')
plt.imshow(img_arr[:,::-1])
让图片数组 行 倒序 - 图片垂直翻转180°
import matplotlib.pyplot as plt
img_arr = plt.imread('./落日.jpg')
plt.imshow(img_arr[::-1])
图片颜色变化
import matplotlib.pyplot as plt
img_arr = plt.imread('./落日.jpg')
plt.imshow(img_arr[::-1,::-1])
2.7 图片裁剪
import matplotlib.pyplot as plt
img_arr = plt.imread('./落日.jpg')
plt.imshow(img_arr[120:210,200:300]) # 根据jupyter的标尺进行切割
2.8 变形
arr
array([[28, 74, 88, 9, 15, 18],
[80, 71, 88, 11, 17, 46],
[ 7, 75, 28, 33, 84, 96],
[88, 44, 5, 4, 71, 88],
[88, 50, 54, 34, 15, 77]])
将多维数组变形成一维数组 - 需要根据原数组的元素个数进行合成
arr.reshape(30)
将多维数组变形成二维数组 - 需要根据原数组的元素个数进行合成
arr.reshape((10,3)
arr.reshape((10,-1) # 自动计算
2.9 级联
- 一维,二维,多维数组的级联,实际操作中级联多为二维数组
import numpy as np
np.concatenate((arr,arr),axis=1) # axis轴 0表示竖轴拼合,1表示横轴拼合 arr为;两个数组
-
合并两张图片
import numpy as np arr_3 = np.concatenate((img_arr,img_arr,img_arr),axis=1) plt.imshow(arr_3) # 三联图 arr_9 = np.concatenate((arr_3,arr_3,arr_3),axis=0) plt.imshow(arr_9) # 九宫格图
-
级联需要注意的点:
1. 级联的参数是列表:一定要加中括号或小括号 2. 维度必须相同 3. 形状相符:在维度保持一致的前提下,如果进行横向(axis=1)级联,必须保证进行级联的数组行数保持一致。如果进行纵向(axis=0)级联,必须保证进行级联的数组列数保持一致。 4. 可通过axis参数改变级联的方向
2.10 ndarray的聚合操作
1. 求和 - np.sum
arr :
array([[28, 74, 88, 9, 15, 18],
[80, 71, 88, 11, 17, 46],
[ 7, 75, 28, 33, 84, 96],
[88, 44, 5, 4, 71, 88],
[88, 50, 54, 34, 15, 77]])
arr.sum(axis=0) # 纵轴的求总和
# array([291, 314, 263, 91, 202, 325])
arr.sum(axis=1) # 横轴的求总和
# array([232, 313, 323, 300, 318])
2. 最大最小值:np.max()/ np.min()
3. 平均值:np.mean()
4. 其他聚合
Function Name NaN-safe Version Description
np.sum np.nansum Compute sum of elements
np.prod np.nanprod Compute product of elements
np.mean np.nanmean Compute mean of elements
np.std np.nanstd Compute standard deviation
np.var np.nanvar Compute variance
np.min np.nanmin Find minimum value
np.max np.nanmax Find maximum value
np.argmin np.nanargmin Find index of minimum value
np.argmax np.nanargmax Find index of maximum value
np.median np.nanmedian Compute median of elements
np.percentile np.nanpercentile Compute rank-based statistics of elements
np.any N/A Evaluate whether any elements are true
np.all N/A Evaluate whether all elements are true
np.power 幂运算
5. ndarray的排序
np.sort()与ndarray.sort()都可以,但有区别:
- np.sort() 返回一个新的排序好的对象,arr并没有改变
- arr.sort(axis
=
0) arr直接被改变了
np.sort(arr,axis=0) # 对每一列排序
np.sort(arr,axis=1) # 对每一行排序
arr.sort(axis=0) # 对每一列排序,改变原有arr数据
3. Pandas的数据结构
1. Series - 一维
Series是一种类似与一维数组的对象,由下面两个部分组成:
- values:一组数据(ndarray类型)
- index:相关的数据索引标签
使用列表创建Series :
import pandas as pd
from pandas import Series,DataFrame
s = Series(data=[3,,4])
#
0 3
1 4
dtype: int64 # 0,1 表示的是隐式索引(默认索引)
s = Series(data=[5,6],index=['a','b']) # a,b表示的是显式索引
#
a 5
b 6
dtype: int64
s['a'] # 5
s[0] # 5
s.a # 5 但不能s.1
2. Series的索引和切片
可以使用中括号取单个索引(此时返回的是元素类型),或者中括号里一个列表取多个索引(此时返回的是一个Series类型)。
(1) 显式索引:
- 使用index中的元素作为索引值
- 使用s.loc[](推荐):注意,loc中括号中放置的一定是显示索引
注意,此时是闭区间
(2)在运算中自动对齐不同索引的数据 如果索引不对应,则补NaN
s['a'] # 5
s[0] # 5
s.a # 5 但不能s.1
s[0:2] # 切片
# 切片结果
a 5
b 6
dtype: int64
可以使用s.head(),tail()分别查看前n个和后n个值
s = Series(data=[1,2,3,4,5,6,9,9,9,])
s.head(2) # 只显示前两行数据
对Series元素进行去重
s = Series(data=[1,2,3,4,5,6,9,9,9,])
s.unique() # 去重
Series相加
s1 = Series([1,2,3],index=['a','b','c'])
s2 = Series([1,2,3],index=['a','d','c'])
s = s1 + s2
s
# 结果
a 2.0
b NaN
c 6.0
d NaN
dtype: float64
# 说明 Series只可以对索引相同的进行算法运算,否则为空(NaN)
可以使用pd.isnull(),pd.notnull(),或s.isnull(),notnull()函数检测缺失数据
s[[True,False,True,False]]
# 结果
a 2.0
c 6.0
dtype: float64
# 将布尔值作为Series的索引,那么索引为False对应的值就不会显示
s.isnull() # 如果是空值返回为True
# 结果
a False
b True
c False
d True
dtype: bool
s.notnull() # 如果不是空返回True
# 结果
a True
b False
c True
d False
dtype: bool
对Series去空 - 将Series中的空值直接进行了清洗
s[s.notnull()]
a 2.0
c 6.0
dtype: float64
# 利用 - 将布尔值作为Series的索引,那么索引为False对应的值就不会显示
3. DataFrame
DataFrame是一个【表格型】的数据结构。DataFrame由按一定顺序排列的多列数据组成。设计初衷是将Series的使用场景从一维拓展到多维。DataFrame既有行索引,也有列索引。
- 行索引:index
- 列索引:columns
- 值:values
3.1 DataFrame的创建
最常用的方法是传递一个字典来创建。DataFrame以字典的键作为每一【列】的名称,以字典的值(一个数组)作为每一列。
此外,DataFrame会自动加上每一行的索引。
使用字典创建的DataFrame后,则columns参数将不可被使用。
同Series一样,若传入的列与字典的键不匹配,则相应的值为NaN。
from pandas import Series,DataFrame
import numpy as np
DataFrame(data=np.random.randint(0,100,size=(2,3)))
# 结果
0 1 2
0 88 15 6
1 85 22 11
from pandas import Series,DataFrame
import numpy as np
df = DataFrame(data=np.random.randint(0,100,size=(2,3)),index=['a','b'],columns=['A','B','C'])
# 结果
A B C
a 12 92 96
b 62 57 79
3.2 DataFrame的基本属性
# values 返回所有元素
df.values # 返回的是一个数组
array([[42, 57, 97],
[50, 45, 40]])
# index 返回所有的列索引
df.index # Index(['a', 'b'], dtype='object')
# columns 返回所有的行索引
df.columns
# Index(['A', 'B', 'C'], dtype='object')
# shape 返回形状
df.shape
# (2, 3)
利用字典作为数据创建DataFrame
dic = {
'zhangsan':[99,99,99,99],
'lisi':[0,0,0,0]
}
df = DataFrame(data=dic,index=['语文','数学','英语','理综'])
# 结果
lisi zhangsan
语文 0 99
数学 0 99
英语 0 99
理综 0 99
3.3 DataFrame的索引
(1) 对列进行索引
- 通过类似字典的方式 df['q']
- 通过属性的方式 df.q
可以将DataFrame的列获取为一个Series。返回的Series拥有原DataFrame相同的索引,且name属性也已经设置好了,就是相应的列名。
取列索引
df['lisi'] #列索引
df[['zhangsan','lisi']]
语文 0
数学 0
英语 0
理综 0
zhangsan lisi
语文 99 0
数学 99 0
英语 99 0
理综 99 0
取行索引
- 使用.loc[]加显式索引取相应行
- 使用.iloc[]加整数来取相应行
df.loc['语文'] 或 df.iloc[0] # 隐式索引 取第一行
lisi 0
zhangsan 99
df.loc[['语文','数学']] 或 df.iloc[[0,1]] # 取前两行
lisi zhangsan
语文 0 99
数学 0 99
取单个某些数据
df.iloc[2,1] #取第三行的第二个值
df.loc['英语','zhangsan']
df.iloc[[1,2],1] # 取第二三行的第二个值
3.4 DataFrame的切片
切行
df[0:2] #行切片
lisi zhangsan
语文 0 99
数学 0 99
切列/行
df.iloc[:,0:1] # 只切列
df.iloc[:2,0:1] # 切前三行和两列
总结:
- 索引
- df['col']:列索引,取出指定的列
- df[[col1,col2]]:取出多列
- df.iloc[1]:取指定的1行
- df.loc['语文']:取指定的行
- df.iloc[hang,lie]:取元素
- 切片:
- df[行切片]:切行
- df.iloc[hang,lie]:切列
3.5 DataFrame的运算
同Series一样:
1. 在运算中自动对齐不同索引的数据
2. 如果索引不对应,则补NaN
可以对DataFrame的每一行每一列直接进行运算
4. 股票分析- 基于tushare模块
Tushare是一个免费、开源的python财经数据接口包。主要实现对股票等金融数据从数据采集、清洗加工 到 数据存储的过程,能够为金融分析人员提供快速、整洁、和多样的便于分析的数据
安装tushare包
pip install tushare
import tushare as ts
import pandas as pd
# 请求茅台股票的数据,600519为茅台股票的代码
maotai = ts.get_k_data(code='600519',start='1900-01-01')
date open close high low volume code
0 1991-01-02 0.185 0.188 0.188 0.185 759.00 000001
1 1991-01-03 0.429 0.429 0.429 0.429 212.40 000001
... ...
# 以maotai.csv为名存储到本地
maotai.to_csv('./maotai.csv')
#读取部分数据(前五行)
maotai.head()
df.index # 显示所有的行索引
df.index[0] # 第一个行索引
df.columns # 显示所有的列索引
df.columns[0] # 第一个列索引
#从本地读取数据 - 用pandas读取数据
df = pd.read_csv('./maotai.csv')
# 删除labels='Unnamed: 0'(字段) axis=1 表示删除列,0表示删除行,
#inplace=True表示操作作用到源数据中
df.drop(labels='Unnamed: 0',axis=1,inplace=True)
#index_col将哪一列作为原数据的行索引 ,parse_dates=['date']:将date的类型转成时间类型
df = pd.read_csv('./maotai.csv',index_col='date',parse_dates=['date'])
要求 :
- 输出该股票所有收盘比开盘上涨3%以上的日期。
# 输出该股票所有收盘比开盘上涨3%以上的日期。
#(收盘-开盘)/开盘 > 0.03 返回的是逻辑表达式,Ture和False
(df['close'] - df['open']) / df['open'] > 0.03
#将True对应的行数据取出
df.loc[(df['close'] - df['open']) / df['open'] > 0.03]
#取行索引
df.loc[(df['close'] - df['open']) / df['open'] > 0.03].index
- 输出该股票所有开盘比前日收盘跌幅超过2%的日期。
#输出该股票所有开盘比前日收盘跌幅超过2%的日期。
#(开盘-前日收盘)/前日收盘 < -0.02
# shift(1)表示下移 ,-1表示上移
(df['open'] - df['close'].shift(1)) / df['close'].shift(1) < -0.02
df.loc[(df['open'] - df['close'].shift(1)) / df['close'].shift(1) < -0.02]
df.loc[(df['open'] - df['close'].shift(1)) / df['close'].shift(1) < -0.02].index
- 假如我从2010年1月1日开始,每月第一个交易日买入1手股票,每年最后一个交易日卖出所有股票,到今天为止,我的收益如何?
df_2010 = df['2010':'2019']
分析 :
- 基于开盘价进行股票的买卖
- 买股票的时机:
- 每月的第一个交易日买入一手(100股)股票
- 一个完整的年会买入12次股票供给1200股
- 卖股票的时机:
- 每年的最后一个交易日卖出所有(1200股)的股票
- 一共可以卖9次股票
- 注意:19年只可以买入不可以卖出,最后剩余的不能卖出的1000股股票是需要计算到总收益中
数据的重新取样resample()
df_2010 = df['2010':'2019']
#买入股票花费的钱数 A年 M天 D天
# m取到每个月对应的第一行数据
df_monthly = df_2010.resample('M').first()
# 买股票花的钱 一手100股
cost_money = df_monthly['open'].sum()*100
#卖出股票收到多少钱,刨除今年
df_yearly = df_2010.resample('A').last()[:-1]
last_price = df_2010['close'][-1] #昨天的收盘价
# 结果:
recv_monry = df_yearly['open'].sum()*1200 + last_price*1000
recv_monry - cost_money
5. 数据清洗 - DataFrame
-
有两种丢失数据:
-
import numpy as np import pandas from pandas import DataFrame
-
None
None是Python自带的,其类型为python object。因此,None不能参与到任何计算中。 #查看None的数据类型 type(None) # NoneType
-
np.nan(NaN)
np.nan是浮点类型,能参与到计算中。但计算的结果总是NaN。 #查看np.nan的数据类型 type(np.nan) # float
-
1. pandas中的None与NaN
创建DataFrame :
df = DataFrame(data=np.random.randint(0,100,size=(10,8)))
df
0 1 2 3
0 62 13 85 96
1 91 66 93 61
2 65 60 40 81
#将某些数组元素赋值为nan / None
df.iloc[1,2] = None
df.iloc[2,1] = np.nan
df
0 1 2 3
0 30 95.0 21.0 88
1 10 54.0 NaN 71
2 17 NaN 30.0 27
判断函数 df.isnull() / df.notnull()
df.isnull()
0 1 2 3
0 False False False False
1 False False True False
2 False True False False
all()
/ any()
df.isnull().all(axis=0) # 鉴定每一列的总状态 all : 有一个False就是False,全True才True
0 False
1 False
2 False
3 False
df.isnull().all(axis=1) # 鉴定每一行的总状态
0 False
1 False
2 False
df.isnull().any(axis=1) # 鉴定每一行的总状态 all : 有一个True就是True
0 False
1 True
2 True
df.loc[~df.isnull().any(axis=1)] # ~ 为取反
df.loc[df.notnull().all(axis=1)] # 两个结果相同
0 1 2 3
0 30 95.0 21.0 88
固定搭配 :
isnull() → any()
notnull() → all()
df.dropna() 可以选择过滤的是行还是列(默认为行):axis中0表示行,1表示的列
df.dropna(axis=0)
0 1 2 3
0 30 95.0 21.0 88
覆盖
df.fillna(value=66666) # 将所有的空值赋值66666
使用周围近邻值覆盖
df.fillna(method='ffill',axis=1) # 竖轴前面一数据 作为填充数据源进行覆盖 bfill为向后
0 1 2 3
0 30.0 95.0 21.0 88.0
1 10.0 54.0 54.0 71.0
2 17.0 17.0 30.0 27.0
#测试df_test中的哪些列中还有空值
df_test.isnull().any(axis=0)
6. pandas的拼接操作
pandas的拼接分为两种:
-
级联:pd.concat, pd.append
-
合并:pd.merge, pd.join
import pandas as pd import numpy as np from pandas import DataFrame,Series
1. 使用pd.concat()级联
pandas使用pd.concat函数的级联,与np.concatenate函数类似,只是多了一些参数:
objs
axis=0
keys
join='outer' / 'inner':表示的是级联的方式,outer会将所有的项进行级联(忽略匹配和不匹配,默认参数),而inner只会将匹配的项级联到一起,不匹配的不级联
ignore_index=False
-
匹配级联
df1 = DataFrame(data=np.random.randint(0,100,size=(3,4))) df2 = DataFrame(data=np.random.randint(0,100,size=(3,4))) pd.concat((df1,df2),axis=0)
-
不匹配级联
不匹配指的是级联的维度的索引不一致。例如纵向级联时列索引不一致,横向级联时行索引不一致
-
pd.concat((df1,df2),axis=0,join='inner')
-
有2种连接方式:
- 外连接:补NaN(默认模式)- outer
- 内连接:只连接匹配的项 - inner
-
2. 使用pd.merge()合并
merge与concat的区别在于,merge需要依据某一共同的列来进行合并
使用pd.merge()合并时,会自动根据两者相同column名称的那一列,作为key来进行合并。
注意每一列元素的顺序不要求一致
参数:
- how:out取并集 inner取交集 - 默认
- on:当有多列相同的时候,可以使用on来指定使用那一列进行合并,on的值为一个列表
如果不写on,默认将相同的一列作为合并条件
1. 一对一合并
数据
df1 = DataFrame({'employee':['Bob','Jake','Lisa'],
'group':['Accounting','Engineering','Engineering'],
})
df1
df2 = DataFrame({'employee':['Lisa','Bob','Jake'],
'hire_date':[2004,2008,2012],
})
df2
pd.merge(df1,df2)
2. 多对一合并
df3 = DataFrame({
'employee':['Lisa','Jake'],
'group':['Accounting','Engineering'],
'hire_date':[2004,2016]})
df3
df4 = DataFrame({'group':['Accounting','Engineering','Engineering'],
'supervisor':['Carly','Guido','Steve']
})
df4
pd.merge(df3,df4)
3. 多对多合并
df1 = DataFrame({'employee':['Bob','Jake','Lisa'],
'group':['Accounting','Engineering','Engineering']})
df1
df5 = DataFrame({'group':['Engineering','Engineering','HR'],
'supervisor':['Carly','Guido','Steve']
})
df5
pd.merge(df1,df5,how='right') # right保留右表数据
4. key的规范化
- 当列冲突时,即有多个列名称相同时,需要使用on=来指定哪一个列作为key,配合suffixes指定冲突列名
df1 = DataFrame({'employee':['Jack',"Summer","Steve"],
'group':['Accounting','Finance','Marketing']})
df2 = DataFrame({'employee':['Jack','Bob',"Jake"],
'hire_date':[2003,2009,2012],
'group':['Accounting','sell','ceo']})
pd.merge(df1,df2,on='employee')
-
当两张表没有可进行连接的列时,可使用left_on和right_on手动指定merge中左右两边的哪一列列作为连接的列
df1 = DataFrame({'employee':['Bobs','Linda','Bill'], 'group':['Accounting','Product','Marketing'], 'hire_date':[1998,2017,2018]}) df1 df5 = DataFrame({'name':['Lisa','Bobs','Bill'], 'hire_dates':[1998,2016,2007]}) df5
pd.merge(df1,df5,left_on='employee',right_on='name',how='outer')
5. 内合并与外合并:out取并集 inner取交集
-
内合并:只保留两者都有的key(默认模式)
df6 = DataFrame({'name':['Peter','Paul','Mary'], 'food':['fish','beans','bread']} ) df7 = DataFrame({'name':['Mary','Joseph'], 'drink':['wine','beer']})
-
外合并 how='outer':补NaN
df6 = DataFrame({'name':['Peter','Paul','Mary'], 'food':['fish','beans','bread']} ) df7 = DataFrame({'name':['Mary','Joseph'], 'drink':['wine','beer']})
3. 案例分析:美国各州人口数据分析
- 需求:
- 导入文件,查看原始数据
- 将人口数据和各州简称数据进行合并
- 将合并的数据中重复的abbreviation列进行删除
- 查看存在缺失数据的列
- 找到有哪些state/region使得state的值为NaN,进行去重操作
- 为找到的这些state/region的state项补上正确的值,从而去除掉state这一列的所有NaN
- 合并各州面积数据areas
- 我们会发现area(sq.mi)这一列有缺失数据,找出是哪些行
- 去除含有缺失数据的行
- 找出2010年的全民人口数据
- 计算各州的人口密度
- 排序,并找出人口密度最高的五个州 df.sort_values()
import numpy as np
from pandas import DataFrame,Series
import pandas as pd
-
导入文件,查看原始数据
abb = pd.read_csv('./data/state-abbrevs.csv') abb.head(2) pop = pd.read_csv('./data/state-population.csv') pop.head(2) area = pd.read_csv('./data/state-areas.csv') area.head(2)
-
将人口数据和各州简称数据进行合并
abb_pop = pd.merge(abb,pop,left_on='abbreviation',right_on='state/region',how='outer') abb_pop.head(2)
-
将合并的数据中重复的abbreviation列进行删除
abb_pop.drop(labels='abbreviation',axis=1,inplace=True) # inplace将操作作用到原表 abb_pop.head(2)
-
查看存在缺失数据的列
abb_pop.isnull().any(axis=0)
-
找到有哪些state/region使得state的值为NaN,进行去重操作
理解 : 找到state的空对应的state/region空值有几种,然后去重
#1.state列中哪些值为空 abb_pop['state'].isnull() #2.可以将step1中空对应的行数据取出(state中的空值对应的行数据) abb_pop.loc[abb_pop['state'].isnull()] #3.将对应的行数据中指定的简称列取出 abb_pop.loc[abb_pop['state'].isnull()]['state/region'].unique()
-
为找到的这些state/region的state项补上正确的值,从而去除掉state这一列的所有NaN
#1.先将USA对应的state列中的空值定位到(找到所有的USA所对应的行) abb_pop['state/region'] == 'USA' # 逻辑表达式,范湖的是True/False #2,将布尔值作为原数据的行索引,取出USA简称对应的行数据 abb_pop.loc[abb_pop['state/region'] == 'USA'] #3.获取符合要求行数据的行索引 indexs = abb_pop.loc[abb_pop['state/region'] == 'USA'].index #4.将indexs这些行中的state列的值批量赋值成united states abb_pop.loc[indexs,'state'] = 'United Status' #5.将PR对应的state列中的空批量赋值成 PUERTO RICO abb_pop['state/region'] == 'PR' abb_pop.loc[abb_pop['state/region'] == 'PR'] indexs = abb_pop.loc[abb_pop['state/region'] == 'PR'].index abb_pop.loc[indexs,'state'] = 'PUERTO RICO'
-
合并各州面积数据areas
abb_pop_area = pd.merge(abb_pop,area,how='outer') abb_pop_area.head(3)
-
我们会发现area(sq.mi)这一列有缺失数据,找出是哪些行
abb_pop_area['area (sq. mi)'].isnull() #将空值对应的行数据取出 indexs = abb_pop_area.loc[abb_pop_area['area (sq. mi)'].isnull()].index indexs
-
去除含有缺失数据的行
abb_pop_area.drop(labels=indexs,axis=0,inplace=True)
-
找出2010年的全民人口数据
abb_pop_area.query('year == 2010 & ages == "total"')
-
计算各州的人口密度
abb_pop_area['midu'] = abb_pop_area['population'] / abb_pop_area['area (sq. mi)'] abb_pop_area.head(2)
-
排序,并找出人口密度最高的五个州 df.sort_values()
abb_pop_area.sort_values(by='midu',axis=0,ascending=False).head(5)
# ascending=False升序
7. pandas数据处理
- 使用duplicated()函数检测重复的行,返回元素为布尔类型的Series对象,每个元素对应一行,如果该行不是第一次出现,则元素为True
- keep参数:指定保留哪一重复的行数据
import numpy as np
import pandas as pd
from pandas import DataFrame
创建具有重复元素行的DataFrame
#创建一个df
df = DataFrame(data=np.random.randint(0,100,size=(12,7)))
df
#手动将df的某几行设置成相同的内容
df.iloc[1] = [6,6,6,6,6,6,6]
df.iloc[8] = [6,6,6,6,6,6,6]
df.iloc[5] = [6,6,6,6,6,6,6]
df
1. 删除重复数据(行级别)
-
使用drop_duplicates()函数删除重复的行
- drop_duplicates(keep='first/last'/False)
- first删除第一行的重复数据
- last删除最后一行的重复数据
- False删除所有行的重复数据
df.drop_duplicates(keep='last')
2. 映射
DataFrame替换操作 :
- 单值替换
- 普通替换: 替换所有符合要求的元素:to_replace=15,value='e'
- 按列指定单值替换: to_replace={列标签:替换值} value='value'
- 多值替换
- 列表替换: to_replace=[] value=[]
- 字典替换(推荐) to_replace={to_replace:value,to_replace:value}
- 注意:DataFrame中,无法使用method和limit参数
替换 :
#创建一个df
df = DataFrame(data=np.random.randint(0,100,size=(12,7)))
df
将源数据中所有的6都替换成six
df.replace(to_replace=6,value='six') # 加入inplace=True就会改写源数据
df.replace(to_replace={6:'six'})
将第五列中的6替换成six
df.replace(to_replace={5:6},value='six') # to_replace={5:6} 第五列中的6
映射 :
-
map()函数:新建一列 , map函数并不是df的方法,而是series的方法
-
map()可以映射新一列数据
-
map()中可以使用lambd表达式
-
map()中可以使用方法,可以是自定义的方法
eg:map({to_replace:value})
-
注意 map()中不能使用sum之类的函数,for循环
新增一列:给df中,添加一列,该列的值为中文名对应的英文名
dic = {
'name':['张三','周杰伦','张三'],
'salary':[20000,10000,20000]
}
df = DataFrame(data=dic)
df
#映射关系表
dic = {
'张三':'tom',
'周杰伦':'jay'
}
df['e_name'] = df['name'].map(dic)
df
- map当做一种运算工具,至于执行何种运算,是由map函数的参数决定的(参数:lambda,函数)
使用自定义函数 :
# 自定义的函数
def after_sal(s):
return s - (s-3000)*0.5
#超过3000部分的钱缴纳50%的税
df['after_sal'] = df['salary'].map(after_sal)
df
df['after_sal'] = df['salary'].apply(after_sal) #apply效率高于map
df
注意:并不是任何形式的函数都可以作为map的参数。只有当一个函数具有一个参数且有返回值,那么该函数才可以作为map的参数
8. 使用聚合操作对数据异常值检测和过滤
- 使用df.std()函数可以求得DataFrame对象每一列的标准差
创建一个1000行3列的df 范围(0-1),求其每一列的标准差
df = DataFrame(data=np.random.random(size=(1000,3)),columns=['A','B','C'])
df
对df应用筛选条件,去除标准差太大的数据:假设过滤条件为 C列数据大于两倍的C列标准差
twice_std = df['C'].std() * 2
twice_std
~(df['C'] > twice_std)
df.loc[~(df['C'] > twice_std)]
- 检测过滤缺失值
- dropna
- fillna
- 检测过滤重复值
- drop_duplicated(keep)
- 检测过滤异常值
- 得到鉴定异常值的条件
- 将异常值对应的行删除
9. 排序
使用.take()函数排序
- take()函数接受一个索引列表,用数字表示,使得df根据列表中索引的顺序进行排序
- eg:df.take([1,3,4,2,5])
可以借助np.random.permutation()函数随机排序
np.random.permutation(x)可以生成x个从0-(x-1)的随机数列
np.random.permutation(1000)
df.take(indices=np.random.permutation(1000),axis=0).take(indices=np.random.permutation(3),axis=1)
# 切五行
df.take(indices=np.random.permutation(1000),axis=0).take(indices=np.random.permutation(3),axis=1)[0:5]
- 随机抽样
- 当DataFrame规模足够大时,直接使用np.random.permutation(x)函数,就配合take()函数实现随机抽样
10. 数据分类处理【重点】
数据聚合是数据处理的最后一步,通常是要使每一个数组生成一个单一的数值。
数据分类处理:
- 分组:先把数据分为几组
- 用函数处理:为不同组的数据应用不同的函数以转换数据
- 合并:把不同组得到的结果合并起来
数据分类处理的核心:
- groupby()函数
- groups属性查看分组情况
- eg: df.groupby(by='item').groups
分组
df = DataFrame({'item':['Apple','Banana','Orange','Banana','Orange','Apple'],
'price':[4,3,3,2.5,4,2],
'color':['red','yellow','yellow','green','green','green'],
'weight':[12,20,50,30,20,44]})
df
-
使用groupby实现分组
df.groupby(by='item',axis=0) #根据item分组
-
使用groups查看分组情况
#该函数可以进行数据的分组,但是不显示分组情况 df.groupby(by='item',axis=0).groups
-
分组后的聚合操作:分组后的成员中可以被进行运算的值会进行运算,不能被运算的值不进行运算
-
给df创建一个新列,内容为各个水果的平均价格
df.groupby(by='item',axis=0).mean()['price'] # 这样效率低 # 先将价格取出再运算 df.groupby(by='item',axis=0)['price'].mean()
按颜色查看各种颜色的水果的平均价格 mean_price_series = df.groupby(by='item',axis=0)['price'].mean() mean_price_series # 结果 item Apple 3.00 Banana 2.75 Orange 3.50 #映射关系表 dic = mean_price_series.to_dict() # 将 mean_price_series 转换成字典作为映射表 df['mean_price'] = df['item'].map(dic) df
-
汇总:将各种颜色水果的平均价格和df进行汇总
df['color_mean_price'] = df['color'].map(df.groupby(by='color')['price'].mean().to_dict()) df
11. 高级数据聚合
使用groupby分组后,也可以使用transform和apply提供自定义函数实现更多的运算
- df.groupby('item')['price'].sum() <==> df.groupby('item')['price'].apply(sum)
- transform和apply都会进行运算,在transform或者apply中传入函数即可
- transform和apply也可以传入一个lambda表达式
自定义聚合函数 :
def my_mean(s):
sum = 0
for i in s:
sum += i
return sum/len(s)
#使用apply函数求出水果的平均价格
df.groupby(by='item')['price'].apply(my_mean)
12. 美国2012年总统候选人政治献金数据分析
import numpy as np
import pandas as pd
from pandas import Series,DataFrame
-
方便大家操作,将月份和参选人以及所在政党进行定义
months = {'JAN' : 1, 'FEB' : 2, 'MAR' : 3, 'APR' : 4, 'MAY' : 5, 'JUN' : 6, 'JUL' : 7, 'AUG' : 8, 'SEP' : 9, 'OCT': 10, 'NOV': 11, 'DEC' : 12} of_interest = ['Obama, Barack', 'Romney, Mitt', 'Santorum, Rick', 'Paul, Ron', 'Gingrich, Newt'] parties = { 'Bachmann, Michelle': 'Republican', 'Romney, Mitt': 'Republican', 'Obama, Barack': 'Democrat', "Roemer, Charles E. 'Buddy' III": 'Reform', 'Pawlenty, Timothy': 'Republican', 'Johnson, Gary Earl': 'Libertarian', 'Paul, Ron': 'Republican', 'Santorum, Rick': 'Republican', 'Cain, Herman': 'Republican', 'Gingrich, Newt': 'Republican', 'McCotter, Thaddeus G': 'Republican', 'Huntsman, Jon': 'Republican', 'Perry, Rick': 'Republican' }
-
读取usa_election.txt文件
df = pd.read_csv('./data/usa_election.txt') df.head()
| | cmte_id | cand_id | cand_nm | contbr_nm | contbr_city | contbr_st | contbr_zip | contbr_employer | contbr_occupation | contb_receipt_amt | contb_receipt_dt | receipt_desc | memo_cd | memo_text | form_tp | file_num |
| ---: | --------: | --------: | -----------------: | --------------: | ----------: | --------: | ---------: | --------------: | ----------------: | ----------------: | ---------------: | -----------: | ------: | --------: | ------: | -------: |
| 0 | C00410118 | P20002978 | Bachmann, Michelle | HARVEY, WILLIAM | MOBILE | AL | 3.6601e+08 | RETIRED | RETIRED | 250.0 | 20-JUN-11 | NaN | NaN | NaN | SA17A | 736166 |
| 1 | C00410118 | P20002978 | Bachmann, Michelle | HARVEY, WILLIAM | MOBILE | AL | 3.6601e+08 | RETIRED | RETIRED | 50.0 | 23-JUN-11 | NaN | NaN | NaN | SA17A | 736166 |
...
2. 新建一列各个候选人所在党派party
```python
# 利用映射
df['party'] = df['cand_nm'].map(parties)
df.head()
cmte_id | cand_id | cand_nm | contbr_nm | contbr_city | contbr_st | contbr_zip | contbr_employer | contbr_occupation | contb_receipt_amt | contb_receipt_dt | receipt_desc | memo_cd | memo_text | form_tp | file_num | party | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | C00410118 | P20002978 | Bachmann, Michelle | HARVEY, WILLIAM | MOBILE | AL | 3.6601e+08 | RETIRED | RETIRED | 250.0 | 20-JUN-11 | NaN | NaN | NaN | SA17A | 736166 | Republican |
1 | C00410118 | P20002978 | Bachmann, Michelle | HARVEY, WILLIAM | MOBILE | AL | 3.6601e+08 | RETIRED | RETIRED | 50.0 | 23-JUN-11 | NaN | NaN | NaN | SA17A | 736166 | Republican |
-
party这一列中有哪些元素
df['party'].unique() # 利用去重
-
统计party列中各个元素出现次数
df['party'].value_counts()
结果 :
Democrat 292400 Republican 237575 Reform 5364 Libertarian 702
-
查看各个党派收到的政治献金总数contb_receipt_amt
df.groupby(by='party')['contb_receipt_amt'].sum()
结果
party Democrat 8.105758e+07 Libertarian 4.132769e+05 Reform 3.390338e+05 Republican 1.192255e+08
-
查看每天各个党派收到的政治献金总数contb_receipt_amt
df.groupby(by=['contb_receipt_dt','party'])['contb_receipt_amt'].sum() # groupby中by的参数,谁在前先对谁分组
-
将表中日期格式转换为'yyyy-mm-dd' day-m-y
def transformDate(d): day,month,year = d.split('-') month = months[month] return '20'+year+'-'+str(month)+'-'+day df['contb_receipt_dt'] = df['contb_receipt_dt'].map(transformDate) df.head()
-
查看老兵(捐献者职业)主要支持谁 :查看老兵们捐赠给谁的钱最多
#1.将老兵对应的行数据取出 df['contbr_occupation'] == 'DISABLED VETERAN' old_bing = df.loc[df['contbr_occupation'] == 'DISABLED VETERAN'] #2.根据候选人分组 old_bing.groupby(by='cand_nm')['contb_receipt_amt'].sum() # 结果 : cand_nm Cain, Herman 300.00 Obama, Barack 4205.00 Paul, Ron 2425.49 Santorum, Rick 250.00
-
捐赠金额最大的人的职业以及捐献额 .通过query("查询条件来查找捐献人职业")
# 1.先找出捐钱金额最大的数据 df['contb_receipt_amt'].max() # 结果 1944042.43 # 2.通过query("查询条件来查找捐献人职业") df.query('contb_receipt_amt == 1944042.43')
13. matplotlib绘图
seaborn - 也是绘图模块
1. Matplotlib基础知识
Matplotlib中的基本图表包括的元素
+ x轴和y轴 axis
水平和垂直的轴线
+ x轴和y轴刻度 tick
刻度标示坐标轴的分隔,包括最小刻度和最大刻度
+ x轴和y轴刻度标签 tick label
表示特定坐标轴的值
+ 绘图区域(坐标系) axes
实际绘图的区域
+ 坐标系标题 title
实际绘图的区域
+ 轴标签 xlabel ylabel
实际绘图的区域
导包 :
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pandas import Series,DataFrame
%matplotlib inline # jupyter中的一个魔法函数(Magic Functions)当你调用matplotlib.pyplot的绘图函数plot()进行绘图的时候,或者生成一个figure画布的时候,可以直接在你的python console里面生成图像。
1. 包含单条曲线的图
x=[1,2,3,4,5]
y=[2,4,6,8,10]
plt.plot(x,y)
2. 绘制抛物线
x = np.linspace(-np.pi,np.pi,num=10) # linspace等差数列 pi=π
y = x**2
plt.plot(x,y)
3. 绘制正弦曲线
x = x
y = np.sin(x)
plt.plot(x,y)
4. 包含多个曲线的图
a. 连续调用多次plot函数
plt.plot(x,y)
plt.plot(x-1,y+2)
b. 在一个plot函数中传入多对X,Y值,在一个图中绘制多个曲线
plt.plot(x,y,x+1,y-1)
5. 将多个曲线图绘制在一个table区域中:对象形式创建表图
- a=plt.subplot(row,col,loc) 创建曲线图
- a.plot(x,y) 绘制曲线图
ax1 = plt.subplot(2,2,1) # 2,2,1 表示绘制两行两列的曲线图,在位置1上
ax1.plot(x,y)
ax2 = plt.subplot(222)
ax2.plot(x+1,y-2)
ax3 = plt.subplot(223)
ax3.plot(x+3,y-1)
ax4 = plt.subplot(224)
ax4.plot(x**2,y-2)
6. 坐标轴界限 (设置坐标轴范围)
plt.axis([xmin,xmax,ymin,ymax])
plt.plot(x,y)
plt.axis([-6,6,-2,2]) # 坐标轴的范围是 : X轴 (-6,6), Y轴 (-2,2)
7. 关闭坐标轴 - plt.axis('off')
plt.plot(x,y)
plt.axis('off')
8. 设置画布比例
- plt.figure(figsize=(a,b)) a:x刻度比例 b:y刻度比例 (2:1)表示x刻度显示为y刻度显示的2倍
plt.figure(figsize=(16,8))
plt.plot(x,y)
9. 坐标轴标签
- s 标签内容
- color 标签颜色
- fontsize 字体大小
- rotation 旋转角度
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['FangSong'] # 指定默认字体 解决不能显示中文的问题
mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
plt.plot(x,y)
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.title('标题')
10. 图例
两种传参方法:
- 分别在plot函数中增加label参数,再调用plt.legend()方法显示
- 直接在legend方法中传入字符串列表
plt.plot(x,y,label='temp')
plt.plot(x-1,y+3,label='dist')
plt.legend(loc=4)
plt.legend(loc=3,ncol=2)
# loc 为指定图例的位置默认是1 ; ncol控制图例中有几列,在legend中设置ncol
1 - 右上
2 - 左上
3 - 左下
4 - 右下
11. 保存图片
-
使用figure对象的savefig函数来保存图片
-
fig = plt.figure()---必须放置在绘图操作之前
-
figure.savefig的参数选项
-
filename
含有文件路径的字符串或Python的文件型对象。图像格式由文件扩展名推断得出,例如,.pdf推断出PDF,.png推断出PNG (“png”、“pdf”、“svg”、“ps”、“eps”……)
-
dpi
图像分辨率(每英寸点数),默认为100
-
facecolor ,打开保存图片查看 图像的背景色,默认为“w”(白色)
-
fig = plt.figure()
plt.plot(x,y,label='temp')
plt.plot(x-1,y+3,label='dist')
plt.legend(loc=3,ncol=2)
fig.savefig('./123.png',dpi=300)
12. 设置plot的风格和样式
-
设置plot的风格和样式
- plot语句中支持除X,Y以外的参数,以字符串形式存在,来控制颜色、线型、点型等要素,语法形式为:
plt.plot(X, Y, 'format', ...)
plt.plot(x,y,c='red',alpha=0.5,ls='steps',lw=3,marker='s',markersize='10') # 颜色 : color/c # alpha : 透明度 # 线性 : linestyle或ls # 线宽 : linewidth或lw参数 # 点型 marker 设置点形 markersize 设置点形大小
线条风格 描述 线条风格 描述 '-' 实线 ':' 虚线 '--' 破折线 'steps' 阶梯线 '-.' 点划线 'None' / ',' 什么都不画 - plot语句中支持除X,Y以外的参数,以字符串形式存在,来控制颜色、线型、点型等要素,语法形式为:
点型 :
标记 | 描述 | 标记 | 描述 | 标记 | 描述 | 标记 | 描述 |
---|---|---|---|---|---|---|---|
's' | 正方形 | 'p' | 五边形 | 'x' | X | ',' | 像素 |
'h' | 六边形1 | 'H' | 六边形2 | '*' | 星号 | 'o' | 圆圈 |
'8' | 八边形 | '.' | 点 | '+' | 加号 | 'D' | 菱形 |
'1' | 一角朝下的三脚架 | '2' | 一角朝上的三脚架 | '3' | 一角朝左的三脚架 | '4' | 一角朝右的三脚架 |
'd' | 小菱形 | '','None',' ',None | 无 |
2. 2D图形
1. 直方图
- 是一个特殊的柱状图,又叫做密度图。
【直方图的参数只有一个x!!!不像条形图需要传入x,y】
plt.hist()的参数
- bins
直方图的柱数,可选项,默认为10 - color
指定直方图的颜色。可以是单一颜色值或颜色的序列。如果指定了多个数据集合,例如DataFrame对象,颜色序列将会设置为相同的顺序。如果未指定,将会使用一个默认的线条颜色 - orientation
通过设置orientation为horizontal创建水平直方图。默认值为vertical
data = [1,2,3,3,4,2,5]
plt.hist(data,bins=10,color='red')
plt.hist(data,bins=10,color='red',orientation='horizontal')
返回值 :
1: 直方图向量,是否归一化由参数normed设定
2: 返回各个bin的区间范围
3: 返回每个bin里面包含的数据,是一个list
2. 条形图
-
参数:第一个参数是索引。第二个参数是数据值。第三个参数是条形的宽度
-
【条形图有两个参数x,y】
-
width 纵向设置条形宽度
-
height 横向设置条形高度
-
bar() - 竖向、barh() - 横向
x = [1,2,3,4,5] y = [2,4,6,8,10] plt.bar(x,y) # 竖向 plt.barh(x,y) # 横向
3. 饼图
【饼图也只有一个参数x】
pie()
饼图适合展示各部分占总体的比例,条形图适合比较各部分的大小
-
普通各部分占满饼图
plt.pie([1,3,5])
-
普通未占满饼图:小数/比例
plt.pie([0.2,0.3,0.1])
饼图阴影、分裂等属性设置 : #labels参数设置每一块的标签; #labeldistance参数设置标签距离圆心的距离(比例值) #autopct参数设置比例值小数保留位(%.3f%%); #pctdistance参数设置比例值文字距离圆心的距离 #explode参数设置每一块顶点距圆心的长度(比例值,列表); #colors参数设置每一块的颜色(列表); #shadow参数为布尔值,设置是否绘制阴影 #startangle参数设置饼图起始角度
-
labels标签
arr=[11,22,31,15] plt.pie(arr,labels=['a','b','c','d']) #
-
参数设置标签距离圆心的距离(比例值)
#labeldistance参数设置标签距离圆心的距离(比例值) arr=[11,22,31,15] plt.pie(arr,labels=['a','b','c','d'],labeldistance=0.3)
-
参数设置比例值小数保留位
#autopct参数设置比例值小数保留位(%.3f%%); 3表示保留三位小数 arr=[11,22,31,15] plt.pie(arr,labels=['a','b','c','d'],labeldistance=0.3,autopct='%.5f%%')
-
explode参数设置每一块顶点距圆心的长度(比例值,列表)
#explode参数设置每一块顶点距圆心的长度(比例值,列表); arr=[11,22,31,15] plt.pie(arr,labels=['a','b','c','d'],labeldistance=0.3,shadow=True,explode=[0.2,0.3,0.2,0.9])
3. 散点图
- 因变量随自变量而变化的大致趋势
【散点图需要两个参数x,y,但此时x不是表示x轴的刻度,而是每个点的横坐标!】- scatter()
x=[1,2,3,4,5]
y=[2,4,6,8,10]
plt.scatter(x,y)
plt.scatter(x,y,marker='d',c="rbgy") 设置不同的散点颜色
x = np.random.random(size=(30,))
y = np.random.random(size=(30,))
plt.scatter(x,y)