pandas 数据分析-【学习笔记-持续更新】

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df1 = pd.DataFrame(np.arange(1000, 1100, 4).reshape(5,5), index=['a'+str(i) for i in range(5)], columns=['b'+str(j) for j in range(5)])
df1
b0b1b2b3b4
a010001004100810121016
a110201024102810321036
a210401044104810521056
a310601064106810721076
a410801084108810921096
df2 = pd.Series([1, 2, 3, np.nan, 5], index=list('abcde'))
df2
a    1.0
b    2.0
c    3.0
d    NaN
e    5.0
dtype: float64

使用传递的numpy数组创建数据帧,并使用日期索引和标记列.

dates = pd.date_range('20190524',periods=6)
dates
print('-'*30)
df3 = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD'))
df3
------------------------------
ABCD
2019-05-24-0.860206-0.549105-1.357905-0.847048
2019-05-251.913799-0.655915-0.638752-0.459323
2019-05-260.249178-1.658453-2.278093-0.745429
2019-05-270.4521180.527087-0.298735-1.872822
2019-05-28-1.4724500.1246421.554257-0.085878
2019-05-29-2.175467-0.611948-0.062950-0.709390

使用传递的可转换序列的字典对象创建数据帧.

df4 = pd.DataFrame({
        'id' : np.arange(4),
        'date' : pd.Timestamp('20190524'),
        'type' : pd.Series(1,index=list(range(4)),dtype='float32'),
        'D' : np.array([3] * 4,dtype='int32'),
        'E' : pd.Categorical(["test","train","test","train"]),
        'F' : 'foo'
    })
df4
iddatetypeDEF
002019-05-241.03testfoo
112019-05-241.03trainfoo
222019-05-241.03testfoo
332019-05-241.03trainfoo

单看index, columns, values

df4.index, df4.columns
(Int64Index([0, 1, 2, 3], dtype='int64'),
 Index(['id', 'date', 'type', 'D', 'E', 'F'], dtype='object'))
df3.values
array([[-0.86020569, -0.5491055 , -1.3579045 , -0.84704786],
       [ 1.91379872, -0.65591487, -0.63875153, -0.4593228 ],
       [ 0.24917803, -1.65845292, -2.27809285, -0.74542856],
       [ 0.45211825,  0.52708701, -0.29873468, -1.87282174],
       [-1.47245001,  0.12464238,  1.55425716, -0.08587762],
       [-2.17546674, -0.61194779, -0.06294959, -0.70938972]])

显示数据快速统计摘要

df3.describe()
ABCD
count6.0000006.0000006.0000006.000000
mean-0.315505-0.470615-0.513696-0.786648
std1.4813900.7511551.2936180.598216
min-2.175467-1.658453-2.278093-1.872822
25%-1.319389-0.644923-1.178116-0.821643
50%-0.305514-0.580527-0.468743-0.727409
75%0.401383-0.043795-0.121896-0.521840
max1.9137990.5270871.554257-0.085878
df4.describe()
idtypeD
count4.0000004.04.0
mean1.5000001.03.0
std1.2909940.00.0
min0.0000001.03.0
25%0.7500001.03.0
50%1.5000001.03.0
75%2.2500001.03.0
max3.0000001.03.0

转置

df4.T
0123
id0123
date2019-05-24 00:00:002019-05-24 00:00:002019-05-24 00:00:002019-05-24 00:00:00
type1111
D3333
Etesttraintesttrain
Ffoofoofoofoo

按值排序

df3.sort_values(by='B')
ABCD
2019-05-260.249178-1.658453-2.278093-0.745429
2019-05-251.913799-0.655915-0.638752-0.459323
2019-05-29-2.175467-0.611948-0.062950-0.709390
2019-05-24-0.860206-0.549105-1.357905-0.847048
2019-05-28-1.4724500.1246421.554257-0.085878
2019-05-270.4521180.527087-0.298735-1.872822

按轴排序

df3.sort_index(axis=1, ascending=False)
DCBA
2019-05-24-0.847048-1.357905-0.549105-0.860206
2019-05-25-0.459323-0.638752-0.6559151.913799
2019-05-26-0.745429-2.278093-1.6584530.249178
2019-05-27-1.872822-0.2987350.5270870.452118
2019-05-28-0.0858781.5542570.124642-1.472450
2019-05-29-0.709390-0.062950-0.611948-2.175467

选择-按标签选择 df.loc[]

# 切片 索引
print(df3['A'])
print('-'*30)
print(df4['E'])
print('-'*30)
print(df3[1:3])
print('-'*30)
print(df3['20190524':'20190525'])
2019-05-24   -0.860206
2019-05-25    1.913799
2019-05-26    0.249178
2019-05-27    0.452118
2019-05-28   -1.472450
2019-05-29   -2.175467
Freq: D, Name: A, dtype: float64
------------------------------
0     test
1    train
2     test
3    train
Name: E, dtype: category
Categories (2, object): [test, train]
------------------------------
                   A         B         C         D
2019-05-25  1.913799 -0.655915 -0.638752 -0.459323
2019-05-26  0.249178 -1.658453 -2.278093 -0.745429
------------------------------
                   A         B         C         D
2019-05-24 -0.860206 -0.549105 -1.357905 -0.847048
2019-05-25  1.913799 -0.655915 -0.638752 -0.459323
# 使用标签获取横截面,参考以下示例
dates = pd.date_range('20130101', periods=6)
df = pd.DataFrame(np.arange(1, 25).reshape(6,4), index=dates, columns=list('ABCD'))
df
ABCD
2013-01-011234
2013-01-025678
2013-01-039101112
2013-01-0413141516
2013-01-0517181920
2013-01-0621222324
# 使用标签获取横截面
print(df.loc[dates[0]])
A    1
B    2
C    3
D    4
Name: 2013-01-01 00:00:00, dtype: int32
# 索引切片-选择指定若干列
df.loc[:,['A','B']]
AB
2013-01-0112
2013-01-0256
2013-01-03910
2013-01-041314
2013-01-051718
2013-01-062122
# 选取区域-行列同时切片索引
df.loc['20130102':'20130104', ['A', 'B']]
AB
2013-01-0256
2013-01-03910
2013-01-041314
# 由于切片索引自动降维
df.loc['20130102', ['A', 'B']]
A    5
B    6
Name: 2013-01-02 00:00:00, dtype: int32
# 取单个标量值-两种结果完全一样的方法loc位置, at位于
print(df.loc[dates[4], 'C'])
print(df.at[dates[4],'C'])
19
19

选择-按位置选择 df.iloc[]

# 选第三行。第三条。第三条记录
df.iloc[3]
A    13
B    14
C    15
D    16
Name: 2013-01-04 00:00:00, dtype: int32
# 选取3到5行,1到3列的区域形成新DataFrame
df.iloc[3:5, 1:3]
BC
2013-01-041415
2013-01-051819
# 使用整数偏移定位列表,隔行选择, 可以多选,可以自定义重复
df.iloc[[1,2,2,4],[0,2]]
AC
2013-01-0257
2013-01-03911
2013-01-03911
2013-01-051719
# 灵活使用列表生成条件表达式-疯狂复读重复某一行限定区块
df.iloc[[3 for i in range(5)],[0,2]]
AC
2013-01-041315
2013-01-041315
2013-01-041315
2013-01-041315
2013-01-041315

------------------------学习蚂蚁 pandas----------------------

快速开始-基本使用

import pandas as pd
fpath = './datas/ml-latest-small/ratings.csv'
# 使用 pd.read_csv 读取 csv 数据
ratings = pd.read_csv(fpath)
# 使用 df.head() 查看 dataframe 的前几行
ratings.head()
userIdmovieIdratingtimestamp
0114.0964982703
1134.0964981247
2164.0964982224
31475.0964983815
41505.0964982931
# 使用 df.shape() 查看数据表的形状
ratings.shape # 看起来好像挺大的样子
(100836, 4)
# 列名列表
ratings.columns
Index(['userId', 'movieId', 'rating', 'timestamp'], dtype='object')
# 索引列表
ratings.index # 0-100836 步长1
RangeIndex(start=0, stop=100836, step=1)
# 每一列的数据类型
ratings.dtypes
userId         int64
movieId        int64
rating       float64
timestamp      int64
dtype: object

1、读取 txt 文件

path1 = './datas/crazyant/access_pvuv.txt'
df1 = pd.read_csv(
    path1, # 源文件路径
    sep='\t', # 以制表符为分隔符分割源文件
    header=None, # 没有字段行,所以要设置 names 
    names=['pdata', 'pv', 'uv'] # 自定义列名列表
)
df1
pdatapvuv
02019-09-1013992
12019-09-09185153
22019-09-0812359
32019-09-076540
42019-09-0615798
52019-09-05205151
62019-09-04196167
72019-09-03216176
82019-09-02227148
92019-09-0110561

读取 excel 文件

path2 = './datas/crazyant/access_pvuv.xlsx'
df2 = pd.read_excel(path2)
df2
日期PVUV
02019-09-1013992
12019-09-09185153
22019-09-0812359
32019-09-076540
42019-09-0615798
52019-09-05205151
62019-09-04196167
72019-09-03216176
82019-09-02227148
92019-09-0110561

读取 sql 数据库数据

import pymysql
# pandas 从数据库读取数据表需要有 mysql 连接
conn = pymysql.connect(
    host='127.0.0.1',
    user='root',
    password='root',
    database='stock',
    charset='utf8'
)
# 从 数据库连接中通过执行原生sql读取数据表
df_sql = pd.read_sql('select * from k_goods', conn)
df_sql
IDPRODUCTNOPRODUCTNAMEPRODUCTTYPEPRODUCTSTANDARDUNITPRICEREMARK
01pdno_0001计算器桌面文具0020None
12pdno_0002回形针桌面文具005None
23pdno_0003中性笔桌面文具005None
34pdno_0004复印纸纸制品0010None
45pdno_0005账本纸制品002None
56pdno_0006拖布办公生活用品0020None
67pdno_0007小蜜蜂扩音器授课设备0025None
78pdno_0008纸杯办公生活用品005None
89pdno_0009白板桌面文具0050None
910pdno_0010软面炒纸制品001None
1011pdno_0011双面胶桌面文具002None
1112pdno_0012文件夹桌面文具003None
1213pdno_0013剪刀文件管理用品008None
1314pdno_0014档案盒文件管理用品0010None
1415pdno_0015电脑办公集写002000None

pandas 数据集结构

  1. Series
  2. DataFrame
  3. 从DataFrame中查询出Series

Series 序列/一维/字典

给定values列表创建Series,默认 index 是range序列

s1 = pd.Series([1,'a',5.2,7])
s1 # 类似字典
0      1
1      a
2    5.2
3      7
dtype: object
# 索引列表 dict.keys()
s1.index
RangeIndex(start=0, stop=4, step=1)
# 值列表 dict.values()
s1.values
array([1, 'a', 5.2, 7], dtype=object)

给定 values列表和index列表创建Series

### 给定 key 和 vlu
s21 = pd.Series(range(5), index=['a','b','c','d','e'])
s22 = pd.Series([x - 2 for x in range(5, 14, 2)], index=['a','b','c','d','e'])
s23 = pd.Series([1, 'a', 5.2, 7, 0.14], ['a','b','c','d','e'])
print(s21)
print(s22)
print(s23)
a    0
b    1
c    2
d    3
e    4
dtype: int64
a     3
b     5
c     7
d     9
e    11
dtype: int64
a       1
b       a
c     5.2
d       7
e    0.14
dtype: object

使用字典创建 Series

sdata={
    'Ohio':35000,
    'Texas':72000,
    'Oregon':16000,
    'Utah':5000
}
s3=pd.Series(sdata)
s3
Ohio      35000
Texas     72000
Oregon    16000
Utah       5000
dtype: int64

根据索引查询数据

print(s3['Ohio'], '\n')
# 给定指定索引列表来索引
print(s3[['Ohio', 'Texas']], '\n') # 只要不是一个都仍是个Series
# 切片索引
print(s3['Ohio':'Utah']) 
35000 

Ohio     35000
Texas    72000
dtype: int64 

Ohio      35000
Texas     72000
Oregon    16000
Utah       5000
dtype: int64

DataFrame 表格/二维/数据表

2. DataFrame

DataFrame是一个表格型的数据结构

  • 每列可以是不同的值类型(数值、字符串、布尔值等)
  • 既有行索引index,也有列索引columns
  • 可以被看做由Series组成的字典

创建dataframe最常用的方法,见02节读取纯文本文件、excel、mysql数据库

根据嵌套的字典创建DataFrame 类似与golang 的 map[string][]interface{}

data={
        'state':['Ohio','Ohio','Ohio','Nevada','Nevada'],
        'year':[2000,2001,2002,2001,2002],
        'pop':[1.5,1.7,3.6,2.4,2.9]
    }
df3 = pd.DataFrame(data)
df3
stateyearpop
0Ohio20001.5
1Ohio20011.7
2Ohio20023.6
3Nevada20012.4
4Nevada20022.9
# 列名列表
df3.columns
Index(['state', 'year', 'pop'], dtype='object')
# 索引列表
df3.index
RangeIndex(start=0, stop=5, step=1)
# 形状/维度
df3.shape
(5, 3)
# 每一列的数据类型
df3.dtypes
state     object
year       int64
pop      float64
dtype: object
# 值
df3.values
array([['Ohio', 2000, 1.5],
       ['Ohio', 2001, 1.7],
       ['Ohio', 2002, 3.6],
       ['Nevada', 2001, 2.4],
       ['Nevada', 2002, 2.9]], dtype=object)

从DataFrame中查询出Series

  • 如果只查询一行、一列,返回的是pd.Series
  • 如果查询多行、多列,返回的是pd.DataFrame
# 索引一列 返回一个Series
df3['year']
0    2000
1    2001
2    2002
3    2001
4    2002
Name: year, dtype: int64
# 索引多列 返回一个DataFrame
df3[['year', 'pop']]
yearpop
020001.5
120011.7
220023.6
320012.4
420022.9
# 查询一列 返回Series
df3.loc[1]
state    Ohio
year     2001
pop       1.7
Name: 1, dtype: object
# 查询指定多列 返回DataFrame
df3.loc[[1:3]]
# 查询范围多列 返回DataFrame
df3.loc[[1, 3]]
stateyearpop
1Ohio20011.7
3Nevada20012.4

pandas 数据查询

Pandas查询数据的几种方法

  1. df.loc方法,根据行、列的标签值查询
  2. df.iloc方法,根据行、列的数字位置查询
  3. df.where方法
  4. df.query方法

.loc既能查询,又能覆盖写入,强烈推荐!

Pandas使用df.loc查询数据的方法

  1. 使用单个label值查询数据
  2. 使用值列表批量查询
  3. 使用数值区间进行范围查询
  4. 使用条件表达式查询
  5. 调用函数查询

注意

  • 以上查询方法,既适用于行,也适用于列
  • 注意观察降维dataFrame>Series>值

0、读取数据

数据为北京2018年全年天气预报
该数据的爬虫教程参见我的Python爬虫系列视频课程

dff = pd.read_csv('./datas/beijing_tianqi/beijing_tianqi_2018.csv')
dff.head()
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
02018-01-013℃-6℃晴~多云东北风1-2级592
12018-01-022℃-5℃阴~多云东北风1-2级491
22018-01-032℃-5℃多云北风1-2级281
32018-01-040℃-8℃东北风1-2级281
42018-01-053℃-6℃多云~晴西北风1-2级501
# 设定指定列为列【日期】,方便按日期筛选
dff.set_index('ymd', inplace=True)
# 按字符串处理
dff.head()
bWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
ymd
2018-01-013℃-6℃晴~多云东北风1-2级592
2018-01-022℃-5℃阴~多云东北风1-2级491
2018-01-032℃-5℃多云北风1-2级281
2018-01-040℃-8℃东北风1-2级281
2018-01-053℃-6℃多云~晴西北风1-2级501
# 替换掉温度的后缀℃
dff.loc[:, "bWendu"] = dff["bWendu"].str.replace("℃", "").astype('int32')
dff.loc[:, "yWendu"] = dff["yWendu"].str.replace("℃", "").astype('int32')
# 再查看,温度列已经转换成了防便计算的数据类型
dff.head()
bWendu        int32
yWendu        int32
tianqi       object
fengxiang    object
fengli       object
aqi           int64
aqiInfo      object
aqiLevel      int64
dtype: object
dff.head()
bWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
ymd
2018-01-013-6晴~多云东北风1-2级592
2018-01-022-5阴~多云东北风1-2级491
2018-01-032-5多云北风1-2级281
2018-01-040-8东北风1-2级281
2018-01-053-6多云~晴西北风1-2级501

1、使用单个label值查询数据

行或者列,都可以只传入单个值,实现精确匹配

# 查询指定行的指定列 得到单个数据
dff.loc['2018-01-03', 'bWendu']
2
# 查询指定行的部分列 得到一个Series
dff.loc['2018-01-03', ['bWendu', 'yWendu']]
bWendu     2
yWendu    -5
Name: 2018-01-03, dtype: object

2、使用值列表批量查询

# 查询部分指定行的指定列 得到Series
df.loc[['2018-01-03','2018-01-04','2018-01-05'], 'bWendu']
bWendu     2
yWendu    -5
Name: 2018-01-03, dtype: object
# 查询区块 得到一个DataFrame
dff.loc[['2018-01-03','2018-01-04','2018-01-05'], ['bWendu', 'yWendu']]
bWenduyWendu
ymd
2018-01-032-5
2018-01-040-8
2018-01-053-6

3、使用数值区间进行范围查询

注意:区间既包含开始,也包含结束

# 查询行区间的指定列 得到Series
dff.loc['2018-01-03':'2018-01-05', 'bWendu']
ymd
2018-01-03    2
2018-01-04    0
2018-01-05    3
Name: bWendu, dtype: int32
# 查寻指定行的列区间 得到Series
dff.loc['2018-01-03', 'bWendu':'fengxiang']
bWendu        2
yWendu       -5
tianqi       多云
fengxiang    北风
Name: 2018-01-03, dtype: object
# 行和列都按区间查询 得到DaTaFrame
dff.loc['2018-01-03':'2018-01-05', 'bWendu':'fengxiang']
bWenduyWendutianqifengxiang
ymd
2018-01-032-5多云北风
2018-01-040-8东北风
2018-01-053-6多云~晴西北风

4、使用条件表达式查询

bool列表的长度得等于行数或者列数

# 观察一下boolean条件 返回布尔对应关系表
bool_map = (dff["yWendu"]<-10)
bool_map.head() # 【单一条件查询,最低温度低于-10度的列表】
ymd
2018-01-01    False
2018-01-02    False
2018-01-03    False
2018-01-04    False
2018-01-05    False
Name: yWendu, dtype: bool
# 根据布尔关系对应表 查询10°C一下的天
dff.loc[bool_map, :]
bWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
ymd
2018-01-23-4-12西北风3-4级311
2018-01-24-4-11西南风1-2级341
2018-01-25-3-11多云东北风1-2级271
2018-12-26-2-11晴~多云东北风2级261
2018-12-27-5-12多云~晴西北风3级481
2018-12-28-3-11西北风3级401
2018-12-29-3-12西北风2级291
2018-12-30-2-11晴~多云东北风1级311
# 观察一下boolean条件 返回布尔对应关系表 【多个布尔对应关系表结合成目标布尔关系对应表】
# 【查询最高温度小于30度,并且最低温度大于15度,并且是晴天,并且天气为优的数据】
bool_map1 = (dff["bWendu"]<=30) & (dff["yWendu"]>=15) & (dff["tianqi"]=='晴') & (dff["aqiLevel"]==1)
bool_map1.head()
ymd
2018-01-01    False
2018-01-02    False
2018-01-03    False
2018-01-04    False
2018-01-05    False
dtype: bool
# 根据布尔关系对应表 查询10°C一下的天查询最高温度小于30度,并且最低温度大于15度,并且是晴天,并且天气为优的数据
dff.loc[bool_map1, :] # 看起来只有两天
bWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
ymd
2018-08-243020北风1-2级401
2018-09-072716西北风3-4级221

5、调用函数查询

# 直接写lambda表达式
dff.loc[lambda dff : (dff["bWendu"]<=30) & (dff["yWendu"]>=15), :]
# 编写自己的函数,查询9月份,空气质量好的数据
def query_my_data(dff):
    return dff.index.str.startswith("2018-09") & df["aqiLevel"]==1
    
dff.loc[query_my_data, :]

pandas 新增数据列

  1. 直接赋值
  2. df.apply
  3. df.assign
  4. 按条件选择分组分别赋值

0、读取csv数据

dfm = pd.read_csv('./datas/beijing_tianqi/beijing_tianqi_2018.csv')
dfm.head()
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
02018-01-013℃-6℃晴~多云东北风1-2级592
12018-01-022℃-5℃阴~多云东北风1-2级491
22018-01-032℃-5℃多云北风1-2级281
32018-01-040℃-8℃东北风1-2级281
42018-01-053℃-6℃多云~晴西北风1-2级501

1、直接赋值的方法

清理温度列变成数字类型

# 替换掉温度的后缀℃
dfm.loc[:, "bWendu"] = dfm["bWendu"].str.replace("℃", "").astype('int32')
dfm.loc[:, "yWendu"] = dfm["yWendu"].str.replace("℃", "").astype('int32')
dfm.head()
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
02018-01-013-6晴~多云东北风1-2级592
12018-01-022-5阴~多云东北风1-2级491
22018-01-032-5多云北风1-2级281
32018-01-040-8东北风1-2级281
42018-01-053-6多云~晴西北风1-2级501
dfm.loc[:, 'wencha'] = dfm['bWendu'] - dfm['yWendu']
dfm.head()
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevelwencha
02018-01-013-6晴~多云东北风1-2级5929
12018-01-022-5阴~多云东北风1-2级4917
22018-01-032-5多云北风1-2级2817
32018-01-040-8东北风1-2级2818
42018-01-053-6多云~晴西北风1-2级5019

2、df.apply 方法

def get_wendu_type(x):
    if x["bWendu"] > 33:
        return '高温'
    if x["yWendu"] < -10:
        return '低温'
    return '常温'

# 注意需要设置axis==1,这是series的index是columns
dfm.loc[:, "wendu_type"] = dfm.apply(get_wendu_type, axis=1) # 传入函数引用,会自动使用函数
# 统计温度类型
dfm["wendu_type"].value_counts()
常温    328
高温     29
低温      8
Name: wendu_type, dtype: int64

3、df.assign 方法

# 可以同时添加多个新的列
dfm = dfm.assign( # assign 方法不会修改 dfm 而是会返回一个新的 DataFrame 对象
    f_yWendu = lambda x : x["yWendu"] * 9 / 5 + 32,
    # 摄氏度转华氏度
    f_bWendu = lambda x : x["bWendu"] * 9 / 5 + 32
)
dfm.head(5)
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevelwenchawendu_typef_yWenduf_bWendu
02018-01-013-6晴~多云东北风1-2级5929常温21.237.4
12018-01-022-5阴~多云东北风1-2级4917常温23.035.6
22018-01-032-5多云北风1-2级2817常温23.035.6
32018-01-040-8东北风1-2级2818常温17.632.0
42018-01-053-6多云~晴西北风1-2级5019常温21.237.4

4、按条件选择分组分别赋值

按条件先选择数据,然后对这部分数据赋值新列
实例:高低温差大于10度,则认为温差大

# 先创建空列(这是第一种创建新列的方法)
dfm['wencha_type'] = '' # 广播的方式,指定所有行的指定列的值都是给定的那个值
# 必须先覆盖全,然后进行分组分配值,不然会造成该列的有的位置缺失值
dfm.loc[dfm['bWendu']-dfm['yWendu'] > 10, 'wencha_type'] = '温差大'

dfm.loc[dfm['bWendu']-dfm['yWendu'] <= 10, 'wencha_type'] = '温差正常'
# 统计温差类型列每种类型数量
dfm['wencha_type'].value_counts()
温差正常    187
温差大     178
Name: wencha_type, dtype: int64

pandas 的数据统计方法

  1. 汇总类统计
  2. 唯一去重和按值计数
  3. 相关系数和协方差
# 先看一下前4行
dfm.head(4)
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevelwenchawendu_typef_yWenduf_bWenduwencha_type
02018-01-013-6晴~多云东北风1-2级5929常温21.237.4温差正常
12018-01-022-5阴~多云东北风1-2级4917常温23.035.6温差正常
22018-01-032-5多云北风1-2级2817常温23.035.6温差正常
32018-01-040-8东北风1-2级2818常温17.632.0温差正常

1、汇总类统计

# 一下子提取所有数字列的各种数字特征的统计结果,更只管把控全局
dfm.describe()
bWenduyWenduaqiaqiLevelwenchaf_yWenduf_bWendu
count365.000000365.000000365.000000365.000000365.000000365.000000365.000000
mean18.6657538.35890482.1835622.09041110.30684947.04602765.598356
std11.85804611.75505351.9361591.0297982.78123321.15909621.344482
min-5.000000-12.00000021.0000001.0000002.00000010.40000023.000000
25%8.000000-3.00000046.0000001.0000008.00000026.60000046.400000
50%21.0000008.00000069.0000002.00000010.00000046.40000069.800000
75%29.00000019.000000104.0000003.00000012.00000066.20000084.200000
max38.00000027.000000387.0000006.00000018.00000080.600000100.400000
# 查看单个数字特征-均值
dfm['bWendu'].mean()
18.665753424657535
# 查看单个数字特征-最大值
dfm[['bWendu', 'yWendu']].max()
bWendu    38
yWendu    27
dtype: int32
# 查看单个数字特征-最小值
dfm['wencha'].min()
2

2、唯一去重和按值计数

唯一去重,适合列出有哪些,不多的枚举
dfm["fengxiang"].unique()
array(['东北风', '北风', '西北风', '西南风', '南风', '东南风', '东风', '西风'], dtype=object)
dfm["tianqi"].unique()
array(['晴~多云', '阴~多云', '多云', '阴', '多云~晴', '多云~阴', '晴', '阴~小雪', '小雪~多云',
       '小雨~阴', '小雨~雨夹雪', '多云~小雨', '小雨~多云', '大雨~小雨', '小雨', '阴~小雨',
       '多云~雷阵雨', '雷阵雨~多云', '阴~雷阵雨', '雷阵雨', '雷阵雨~大雨', '中雨~雷阵雨', '小雨~大雨',
       '暴雨~雷阵雨', '雷阵雨~中雨', '小雨~雷阵雨', '雷阵雨~阴', '中雨~小雨', '小雨~中雨', '雾~多云',
       '霾'], dtype=object)
dfm["fengli"].unique()
array(['1-2级', '4-5级', '3-4级', '2级', '1级', '3级'], dtype=object)
按值计数
dfm["fengxiang"].value_counts()
南风     92
西南风    64
北风     54
西北风    51
东南风    46
东北风    38
东风     14
西风      6
Name: fengxiang, dtype: int64
dfm["tianqi"].value_counts()
晴         101
多云         95
多云~晴       40
晴~多云       34
多云~雷阵雨     14
多云~阴       10
小雨~多云       8
雷阵雨         8
阴~多云        8
雷阵雨~多云      7
小雨          6
多云~小雨       5
雷阵雨~中雨      4
阴           4
中雨~雷阵雨      2
阴~小雨        2
中雨~小雨       2
霾           2
雷阵雨~阴       1
雷阵雨~大雨      1
小雨~雷阵雨      1
小雨~大雨       1
雾~多云        1
小雪~多云       1
阴~雷阵雨       1
小雨~中雨       1
小雨~阴        1
暴雨~雷阵雨      1
小雨~雨夹雪      1
阴~小雪        1
大雨~小雨       1
Name: tianqi, dtype: int64
dfm["fengli"].value_counts()
1-2级    236
3-4级     68
1级       21
4-5级     20
2级       13
3级        7
Name: fengli, dtype: int64

3、相关系数和协方差

用途(超级厉害):

  1. 两只股票,是不是同涨同跌?程度多大?正相关还是负相关?
  2. 产品销量的波动,跟哪些因素正相关、负相关,程度有多大?

来自知乎,对于两个变量X、Y:

  1. 协方差:衡量同向反向程度,如果协方差为正,说明X,Y同向变化,协方差越大说明同向程度越高;如果协方差为负,说明X,Y反向运动,协方差越小说明反向程度越高。
  2. 相关系数:衡量相似度程度,当他们的相关系数为1时,说明两个变量变化时的正向相似度最大,当相关系数为-1时,说明两个变量变化的反向相似度最大
# 协方差矩阵:
dfm.cov()
bWenduyWenduaqiaqiLevelwenchaf_yWenduf_bWendu
bWendu140.613247135.52963347.4626220.8792045.083614243.953339253.103845
yWendu135.529633138.18127416.1866850.264165-2.651641248.726292243.953339
aqi47.46262216.1866852697.36456450.74984231.27593729.13603385.432720
aqiLevel0.8792040.26416550.7498421.0604850.6150380.4754981.582567
wencha5.083614-2.65164131.2759370.6150387.735255-4.7729539.150506
f_yWendu243.953339248.72629229.1360330.475498-4.772953447.707326439.116010
f_bWendu253.103845243.95333985.4327201.5825679.150506439.116010455.586920
# 单独查看空气质量和最高温度的相关系数
dfm["aqi"].corr(dfm["bWendu"])
0.07706705916811067
dfm["aqi"].corr(dfm["yWendu"])
0.026513282672968895
# 空气质量和温差的相关系数-看到了明显提升的相关系数-数据挖掘出了信息-空气质量和温差有关系
dfm["aqi"].corr(dfm["bWendu"] - dfm["yWendu"])
0.2165225757638205
# !! 这就是特征工程对于机器学习重要性的一个例子

Pandas对缺失值的处理

Pandas使用这些函数处理缺失值:

  • isnull和notnull:检测是否是空值,可用于df和series
  • dropna:丢弃、删除缺失值
    • axis : 删除行还是列,{0 or ‘index’, 1 or ‘columns’}, default 0
    • how : 如果等于any则任何值为空都删除,如果等于all则所有值都为空才删除
    • inplace : 如果为True则修改当前df,否则返回新的df
  • fillna:填充空值
    • value:用于填充的值,可以是单个值,或者字典(key是列名,value是值)
    • method : 等于ffill使用前一个不为空的值填充forword fill;等于bfill使用后一个不为空的值填充backword fill
    • axis : 按行还是列填充,{0 or ‘index’, 1 or ‘columns’}
    • inplace : 如果为True则修改当前df,否则返回新的df

读取excel的时候,忽略前几个空行 skiprows 参数控制

studf = pd.read_excel('./datas/student_excel/student_excel.xlsx', skiprows=2)
studf.head(10)
Unnamed: 0姓名科目分数
0NaN小明语文85.0
1NaNNaN数学80.0
2NaNNaN英语90.0
3NaNNaNNaNNaN
4NaN小王语文85.0
5NaNNaN数学NaN
6NaNNaN英语90.0
7NaNNaNNaNNaN
8NaN小刚语文85.0
9NaNNaN数学80.0

检测空值

studf.isnull().head()
Unnamed: 0姓名科目分数
0TrueFalseFalseFalse
1TrueTrueFalseFalse
2TrueTrueFalseFalse
3TrueTrueTrueTrue
4TrueFalseFalseFalse
# 查看单列的空缺情况
studf['分数'].isnull().head()
0    False
1    False
2    False
3     True
4    False
Name: 分数, dtype: bool
# 使用notnull查看非空的
studf['分数'].notnull().head()
0     True
1     True
2     True
3    False
4     True
Name: 分数, dtype: bool
# 筛选没有空分的行-这一步的目的是大致观察清晰的数据
studf.loc[studf['分数'].notnull(), :].head(10)
Unnamed: 0姓名科目分数
0NaN小明语文85.000000
1NaNNaN数学80.000000
2NaNNaN英语90.000000
4NaN小王语文85.000000
6NaNNaN英语90.000000
8NaN小刚语文85.000000
9NaNNaN数学80.000000
10NaNNaN英语90.000000
11NaN小啊语文86.507353
12NaNNaN数学86.654412

删除全是空值的列

studf.dropna(axis=1,how='all' ,inplace=True)
studf.head(10)
姓名科目分数
0小明语文85.0
1NaN数学80.0
2NaN英语90.0
3NaNNaNNaN
4小王语文85.0
5NaN数学NaN
6NaN英语90.0
7NaNNaNNaN
8小刚语文85.0
9NaN数学80.0

删除全是空值的行

studf.dropna(axis=0, how='all', inplace=True)
studf.head(10)
姓名科目分数
0小明语文85.000000
1NaN数学80.000000
2NaN英语90.000000
4小王语文85.000000
5NaN数学NaN
6NaN英语90.000000
8小刚语文85.000000
9NaN数学80.000000
10NaN英语90.000000
11小啊语文86.507353

把分数为空的填充为0

studf['分数'].fillna(0, inplace=True) # 无返回df,直接操作源内村
# studf.fillna({'分数': 0}, inplace=True) # 无返回df,直接操作原内存
# studf.loc[:, '分数'] = studf.fillna({'分数': 0}) # 有返回df
# studf.loc[:, '分数'] = studf['分数'].fillna(0) # 有返回df
# 以上为等效的方法
studf.head(10)
姓名科目分数
0小明语文85.000000
1NaN数学80.000000
2NaN英语90.000000
4小王语文85.000000
5NaN数学0.000000
6NaN英语90.000000
8小刚语文85.000000
9NaN数学80.000000
10NaN英语90.000000
11小啊语文86.507353

将姓名的缺失值填充为上一个

# 参数 ffill代表用上面的值填充,dfill代表用下面的值填充
studf.loc[:, '姓名'] = studf['姓名'].fillna(method="ffill") # 返回填充后的列,赋值给原来的列
# studf['姓名'] = studf['姓名'].fillna(method='ffill')
# studf['姓名'].fillna(method='ffill', inplace=True)
# 以上三种形式等效
studf.head(10)
姓名科目分数
0小明语文85.000000
1小明数学80.000000
2小明英语90.000000
4小王语文85.000000
5小王数学0.000000
6小王英语90.000000
8小刚语文85.000000
9小刚数学80.000000
10小刚英语90.000000
11小啊语文86.507353

将清洗好的数据写入excel文件

studf.to_excel('./datas/student_excel/student_excel_clean.xlsx', index=False) # 舍弃索引列

pandas数据排序

Series的排序:
Series.sort_values(ascending=True, inplace=False)
参数说明:

  • ascending:默认为True升序排序,为False降序排序
  • inplace:是否修改原始Series

DataFrame的排序:
DataFrame.sort_values(by, ascending=True, inplace=False)
参数说明:

  • by:字符串或者List<字符串>,单列排序或者多列排序
  • ascending:bool或者List,升序还是降序,如果是list对应by的多列
  • inplace:是否修改原始DataFrame
df4 = pd.read_csv('./datas/beijing_tianqi/beijing_tianqi_2018.csv')
# 替换掉温度的后缀℃
df4.loc[:, 'bWendu'] = df4['bWendu'].str.replace('℃', '').astype('int32')
df4.loc[:, 'yWendu'] = df4['yWendu'].str.replace('℃', '').astype('int32')
df4.head()
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
02018-01-013-6晴~多云东北风1-2级592
12018-01-022-5阴~多云东北风1-2级491
22018-01-032-5多云北风1-2级281
32018-01-040-8东北风1-2级281
42018-01-053-6多云~晴西北风1-2级501

Series 的排序

按照空气质量排序

# 默认ascending为True代表升序
df4['aqi'].sort_values().head(10) # 也可以指定inplace=True代表在源内存上改
271    21
281    21
249    22
272    22
301    22
246    24
35     24
33     24
10     24
273    25
Name: aqi, dtype: int64
# 下面我们按照降序排序
df4['aqi'].sort_values(ascending=False).head(10) # sort_values 返回结果副本,也可指定inplace
86     387
72     293
91     287
71     287
317    266
329    245
85     243
335    234
57     220
316    219
Name: aqi, dtype: int64
# 按照内部机制排序
df4['tianqi'].sort_values().head(10)
225     中雨~小雨
230     中雨~小雨
197    中雨~雷阵雨
196    中雨~雷阵雨
112        多云
108        多云
232        多云
234        多云
241        多云
94         多云
Name: tianqi, dtype: object


DataFrame 的排序

单列排序

df4.sort_values(by='aqi').head(7) # 按照空气质量升序
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
2712018-09-292211北风3-4级211
2812018-10-09154多云~晴西北风4-5级211
2492018-09-072716西北风3-4级221
2722018-09-301913多云西北风4-5级221
3012018-10-29153北风3-4级221
2462018-09-043118西南风3-4级241
352018-02-050-10北风3-4级241
df4.sort_values(by='aqi', ascending=False).head(7) # 按照空气质量降序
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
862018-03-28259多云~晴东风1-2级387严重污染6
722018-03-14156多云~阴东北风1-2级293重度污染5
712018-03-13175晴~多云南风1-2级287重度污染5
912018-04-022611多云北风1-2级287重度污染5
3172018-11-14135多云南风1-2级266重度污染5
3292018-11-26100多云东南风1级245重度污染5
852018-03-272711南风1-2级243重度污染5

多列排序------显然返回的是df

# 优先按照空气质量等级,次按照最高温排序,默认ascending=True 即[两列都是升序]
df4.sort_values(by=['aqiLevel', 'bWendu']).head(7) 
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
3602018-12-27-5-12多云~晴西北风3级481
222018-01-23-4-12西北风3-4级311
232018-01-24-4-11西南风1-2级341
3402018-12-07-4-10西北风3级331
212018-01-22-3-10小雪~多云东风1-2级471
242018-01-25-3-11多云东北风1-2级271
252018-01-26-3-10晴~多云南风1-2级391
# [两列都是降序]
df4.sort_values(by=['aqiLevel', 'bWendu']).head(7) 
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
3602018-12-27-5-12多云~晴西北风3级481
222018-01-23-4-12西北风3-4级311
232018-01-24-4-11西南风1-2级341
3402018-12-07-4-10西北风3级331
212018-01-22-3-10小雪~多云东风1-2级471
242018-01-25-3-11多云东北风1-2级271
252018-01-26-3-10晴~多云南风1-2级391
# [分别指定升序和降序] ascending传入列表[True, None] None等效False
df4.sort_values(by=['aqiLevel', 'bWendu'], ascending=[True, False]).head(7) 
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
1782018-06-283524多云~晴北风1-2级331
1492018-05-303318西风1-2级461
2062018-07-263325多云~雷阵雨东北风1-2级401
1582018-06-083219多云~雷阵雨西南风1-2级431
2052018-07-253225多云北风1-2级281
2262018-08-153224多云东北风3-4级331
2312018-08-203223多云~晴北风1-2级411

Pandas字符串处理

使用.str转换之后就可以使用python内置的各种字符串处理方法,当然这是pandas封装的,甚至还比标准库多

前面我们已经使用了字符串的处理函数:
df[“bWendu”].str.replace(“℃”, “”).astype(‘int32’)

Pandas的字符串处理:

  1. 使用方法:先获取Series的str属性,然后在属性上调用函数;
  2. 只能在字符串列上使用,不能数字列上使用;
  3. Dataframe上没有str属性和处理方法
  4. Series.str并不是Python原生字符串,而是自己的一套方法,不过大部分和原生str很相似;

Series.str字符串方法列表参考文档:
https://pandas.pydata.org/pandas-docs/stable/reference/series.html#string-handling

本节演示内容:

  1. 获取Series的str属性,然后使用各种字符串处理函数
  2. 使用str的startswith、contains等bool类Series可以做条件查询
  3. 需要多次str处理的链式操作
  4. 使用正则表达式的处理
df5 = pd.read_csv("./datas/beijing_tianqi/beijing_tianqi_2018.csv")
df5.head(7)
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
02018-01-013℃-6℃晴~多云东北风1-2级592
12018-01-022℃-5℃阴~多云东北风1-2级491
22018-01-032℃-5℃多云北风1-2级281
32018-01-040℃-8℃东北风1-2级281
42018-01-053℃-6℃多云~晴西北风1-2级501
52018-01-062℃-5℃多云~阴西南风1-2级321
62018-01-072℃-4℃阴~多云西南风1-2级592
df5.dtypes
ymd          object
bWendu       object
yWendu       object
tianqi       object
fengxiang    object
fengli       object
aqi           int64
aqiInfo      object
aqiLevel      int64
dtype: object

1、获取Series的str属性,使用各种字符串处理函数

# 判断是不是数字的
df5['bWendu'].str.isnumeric().value_counts()
False    365
Name: bWendu, dtype: int64
df5['bWendu'].str.isnumeric().head()
0    False
1    False
2    False
3    False
4    False
Name: bWendu, dtype: bool
# 不可以使用这样的 df5['aqi'].str.len()

2、使用str的startswith、contains等得到bool的Series可以做条件查询

# 条件是以'2018-03'开头 也就是查询了2018年3月的
condition = df5["ymd"].str.startswith("2018-03") 
condition.head() # 前面都是False 到了2018-03就是True了 bool对应关系图
0    False
1    False
2    False
3    False
4    False
Name: ymd, dtype: bool
df5.loc[condition, :].head(7) # 查询三月的
# df5.loc[condition].head(7) # 等效方法
# df5[condition].head(7) # 等效方法
ymdbWenduyWendutianqifengxiangfengliaqiaqiInfoaqiLevel
592018-03-018℃-3℃多云西南风1-2级461
602018-03-029℃-1℃晴~多云北风1-2级952
612018-03-0313℃3℃多云~阴北风1-2级214重度污染5
622018-03-047℃-2℃阴~多云东南风1-2级144轻度污染3
632018-03-058℃-3℃南风1-2级942
642018-03-066℃-3℃多云~阴东南风3-4级672
652018-03-076℃-2℃阴~多云北风1-2级652

3、需要多次str处理的链式操作

怎样提取201803这样的数字月份?
1、replace 先将日期2018-03-31替换成20180331的形式
2、提取月份字符串201803

df5['ymd'].str.replace('-', '').head(7)
0    20180101
1    20180102
2    20180103
3    20180104
4    20180105
5    20180106
6    20180107
Name: ymd, dtype: object
# 每次调用函数,都返回一个新Series,需要再次用 `.str`操作才能继续使用字符串操作方法
# df5["ymd"].str.replace("-", "").slice(0, 6) # 将会报错
df5['ymd'].str.replace('-', '').str.slice(0, 6).head()
# slice方法其实就是切片语法,直接[::]就可
# df5['ymd'].str.replace('-', '').str[0:6]
0    201801
1    201801
2    201801
3    201801
4    201801
Name: ymd, dtype: object

4、使用正则表达式的处理

首先不使用的情况是这样的
# 添加新列-如 2018年03月14日
def get_nianyueri(x):
    year, month, day = x["ymd"].split("-")
    return f"{year}年{month}月{day}日"
df5["中文日期"] = df5.apply(get_nianyueri, axis=1)
df5["中文日期"].head()
0    2018年01月01日
1    2018年01月02日
2    2018年01月03日
3    2018年01月04日
4    2018年01月05日
Name: 中文日期, dtype: object
# 链式操作,每次调用字符串处理方法都要先 `.str`
df5['中文日期'].str.replace('年', '').str.replace('月', '').str.replace('日', '').head()
0    20180101
1    20180102
2    20180103
3    20180104
4    20180105
Name: 中文日期, dtype: object
然后是Series.str默认就开启了正则表达式模式的情况是这样的
# 正则表达式匹配替换-'[年月日]'中括号内每一个字符都匹配
df5['中文日期'].str.replace('[年月日]', '').head()
0    20180101
1    20180102
2    20180103
3    20180104
4    20180105
Name: 中文日期, dtype: object

理解axis参数

axis=0或’index’ 跨行梳出

axis=1或’columns’ 跨列梳出

后续篇目































  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值