pandas学习笔记(五):数据特征分析与pandas优化

注:学习笔记基于文彤老师的pandas的系列课程

课程链接:https://study.163.com/course/courseMain.htm?courseId=1005124008&share=1&shareId=1146477588

# 设定系统环境
import pandas as pd
pd.options.display.max_rows = 10 # 设定自由列表输出最多为10行
pd.__version__ # 显示当前Pandas版本号,默认输出最后一行内容(即使没有打印输出)
'1.1.0'
df2 = pd.read_csv("univ.csv", encoding ="GBK")#使用英文名称,否则可能会报错
#把文件放到了该目录下,因此不需要再写路径,注意编码要写
df2
名次学校名称总分类型所在省份所在城市办学方向主管部门
01北京大学100.00综合北京北京市中国研究型教育部
12清华大学98.50理工北京北京市中国研究型教育部
23复旦大学82.79综合上海上海市中国研究型教育部
34武汉大学82.43综合湖北武汉市中国研究型教育部
45浙江大学82.38综合浙江杭州市中国研究型教育部
...........................
9596浙江师范大学63.37师范浙江金华市区域特色研究型浙江省
9697安徽大学63.34综合安徽合肥市区域研究型安徽省
9798首都医科大学63.32医药北京北京市区域特色研究型北京市
9899江南大学63.31综合江苏无锡市区域特色研究型教育部
99100山西大学63.29综合山西太原市区域研究型山西省

100 rows × 8 columns

1. 数据特征的分析探索

1.1 数值变量的基本描述

df.describe(

percentiles : 需要输出的百分位数,列表格式提供,如[.25, .5, .75]
include = ‘None’ : 要求纳入分析的变量类型白名单
None (default) : 只纳入数值变量列
A list-like of dtypes : 列表格式提供希望纳入的类型
‘all’ : 全部纳入
exclude : 要求剔除出分析的变量类型黑名单,选项同上
)

df2.describe()
名次总分
count100.000000100.000000
mean50.41000068.506100
std28.9657046.766652
min1.00000063.290000
25%25.75000064.387500
50%50.50000065.690000
75%75.25000069.807500
max100.000000100.000000

1.2 分类变量的频数统计

Series.value_counts(

normalize = False : 是否返回构成比而不是原始频数
sort = True : 是否按照频数排序(否则按照原始顺序排列)
ascending = False : 是否升序排列
bins : 对数值变量直接进行分段,可看作是pd.cut的简便用法
dropna = True : 结果中是否包括NaN

)

value_counts是一个序列的函数,但是可以直接用pandas调用

前序列。括号不用加序列
前pd,括号需要加序列

pd.value_counts(df2.类型)
理工    39
综合    32
师范    11
农林     6
医药     5
财经     5
政法     1
民族     1
Name: 类型, dtype: int64
df2.类型.value_counts()
理工    39
综合    32
师范    11
农林     6
医药     5
财经     5
政法     1
民族     1
Name: 类型, dtype: int64
df2.总分.value_counts(bins = 10)
(63.252, 66.961]    64
(66.961, 70.632]    12
(70.632, 74.303]     8
(74.303, 77.974]     7
(81.645, 85.316]     5
(96.329, 100.0]      2
(77.974, 81.645]     2
(92.658, 96.329]     0
(88.987, 92.658]     0
(85.316, 88.987]     0
Name: 总分, dtype: int64

1.3交叉表/数据透视表

数据透视,对多个分类变量进行描述

下面一个是df. 一个是pd. 若是pd 则要指定是哪一个表的哪个变量

df.pivot_table(

行列设定
index / columns : 行变量/列变量,多个时以list形式提供
单元格设定
values : 在单元格中需要汇总的变量列,可不写
aggfunc = numpy.mean : 相应的汇总函数
汇总设定
margins = False : 是否加入行列汇总
margins_name = ‘All’ : 汇总行/列的名称
缺失值处理
fill_value = None : 用于替换缺失值的数值
dropna = True :
)

pd.crosstab(

选项和pivot_table几乎相同
相对而言需要打更多字母,因此使用更麻烦
但是计算频数最方便
输出格式为数据框

行列设定
index / columns : 行变量/列变量,多个时以list形式提供
rownames / colnames = None : 交叉表的行列名称
单元格设定
values : 在单元格中需要汇总的变量列,需要进一步指定aggfunc
aggfunc : 相应的汇总函数
汇总设定
margins = False : 是否加入行列汇总
margins_name = ‘All’ : 汇总行/列的名称
dropna = True :
)

df2.pivot_table(index = ['所在省份', '主管部门'],
           columns = '类型', values = '总分', aggfunc = sum)
类型农林医药师范政法民族理工综合财经
所在省份主管部门
上海NaN64.74NaNNaNNaNNaNNaNNaN
上海市NaNNaNNaNNaNNaNNaN64.41NaN
教育部NaNNaN69.52NaNNaN202.88164.5564.96
云南云南省NaNNaNNaNNaNNaNNaN65.11NaN
北京北京市NaN63.3263.73NaNNaN63.89NaNNaN
..............................
陕西NaN64.51NaNNaNNaNNaNNaNNaN
工业和信息化部NaNNaNNaNNaNNaN67.77NaNNaN
教育部64.92NaN63.88NaNNaN130.9073.56NaN
陕西省NaNNaNNaNNaNNaNNaN65.88NaN
黑龙江工业和信息化部NaNNaNNaNNaNNaN138.13NaNNaN

46 rows × 8 columns

pd.crosstab([df2['所在省份'], df2.主管部门],
            df2.类型, values = df2.总分, aggfunc = sum)
类型农林医药师范政法民族理工综合财经
所在省份主管部门
上海NaN64.74NaNNaNNaNNaNNaNNaN
上海市NaNNaNNaNNaNNaNNaN64.41NaN
教育部NaNNaN69.52NaNNaN202.88164.5564.96
云南云南省NaNNaNNaNNaNNaNNaN65.11NaN
北京北京市NaN63.3263.73NaNNaN63.89NaNNaN
..............................
陕西NaN64.51NaNNaNNaNNaNNaNNaN
工业和信息化部NaNNaNNaNNaNNaN67.77NaNNaN
教育部64.92NaN63.88NaNNaN130.9073.56NaN
陕西省NaNNaNNaNNaNNaNNaN65.88NaN
黑龙江工业和信息化部NaNNaNNaNNaNNaN138.13NaNNaN

46 rows × 8 columns

1.4常用的假设检验方法

相关命令集中在scipy.stats包中,Pandas目前并未考虑做进一步整合,因此仍然需要从Pandas中提取出相应的序列,然后再进行检验

更复杂的分析方法可以在statsmodels中实现,而且statsmodels和Pandas高度整合,直接使用Pandas作为其底层数据结构。

单样本t检验

ss.ttest_1samp(a, popmean[, axis])

两独立样本t检验

ss.ttest_ind(a, b[, axis, equal_var])

配对t检验

ss.ttest_rel(a, b[, axis])

单因素方差分析

ss.f_oneway()

卡方检验

ss.chisquare(f_obs[, f_exp, ddof, axis]) : 单样本卡方
ss.chi2_contingency(observed) : 列联表卡方

相关分析

ss.pearsonr(x,y)

回归分析

ss.linregress(x,y)

非参数检验方法

kstest(rvs, cdf[, args, N, alternative, mode])
Perform the Kolmogorov-Smirnov test for goodness of fit.
ks_2samp(data1, data2)
Computes the Kolmogorov-Smirnov statistic on 2 samples.
rankdata(a[, method])
Assign ranks to data, dealing with ties appropriately.
mannwhitneyu(x, y[, use_continuity])
Computes the Mann-Whitney rank test on samples x and y.
tiecorrect(rankvals)
Tie correction factor for ties in the Mann-Whitney U
and Kruskal-Wallis H tests.
ranksums(x, y)
Compute the Wilcoxon rank-sum statistic for two samples.
wilcoxon(x[, y, zero_method, correction])
Calculate the Wilcoxon signed-rank test.
kruskal(*args)
Compute the Kruskal-Wallis H-test for independent samples
friedmanchisquare(*args)
Computes the Friedman test for repeated measurements

from scipy import stats as ss
# t 检验
ss.ttest_ind(df2.名次, df2.总分) # 各组分别占一列
Ttest_indResult(statistic=-6.083626293702289, pvalue=5.966498524498979e-09)
# ANOVA
ss.f_oneway(df2.名次, df2.总分) # 各组分别占一列
F_onewayResult(statistic=37.01050888142585, pvalue=5.966498524499011e-09)
# 卡方检验
ss.chisquare(df2.类型.value_counts())
Power_divergenceResult(statistic=120.32, pvalue=6.571360136858322e-23)
# 相关系数
ss.pearsonr(df2.名次, df2.总分)
(-0.793031011049869, 8.101456041566422e-23)
# 简单线性回归
ss.linregress(df2.名次, df2.总分)
LinregressResult(slope=-0.1852592629691078, intercept=77.84501944627273, rvalue=-0.7930310110498688, pvalue=8.101456041566526e-23, stderr=0.014375510065991479)

实战:分析PM2.5数据

基于前面数据整理实战中的成果,要求:

给出分年度的数据基本描述
给出分月份的数据基本描述
按照年月交叉,给出PM2.5的最大值
bj.groupby('Year').describe()
MonthDay...HourValue
countmeanstdmin25%50%75%maxcountmean...75%maxcountmeanstdmin25%50%75%max
Year
20085087.07.2227252.0194034.05.07.09.011.05087.015.873403...17.5023.05087.028.405937248.882013-999.032.066.0119.0610.0
20098760.06.5260273.4480481.04.07.010.012.08760.015.720548...17.2523.08760.0-147.127626466.460213-999.011.058.0118.0712.0
20108760.06.5260273.4480481.04.07.010.012.08760.015.720548...17.2523.08760.019.806279306.102714-999.026.071.0139.0980.0
20118760.06.5260273.4480481.04.07.010.012.08760.015.720548...17.2523.08760.07.961530315.760516-999.021.061.0129.0595.0
20128784.06.5136613.4514301.04.07.010.012.08784.015.756831...17.2523.08784.029.864185262.139746-999.020.063.0128.0994.0
20138760.06.5260273.4480481.04.07.010.012.08760.015.720548...17.2523.08760.091.407648144.097932-999.030.071.0137.0886.0
20148760.06.5260273.4480481.04.07.010.012.08760.015.720548...17.2523.08760.085.339498148.629825-999.027.070.5132.0671.0
20158760.06.5260273.4480481.04.07.010.012.08760.015.720548...17.2523.08760.071.658904139.751292-999.021.053.0108.0722.0
20168784.06.5136613.4514301.04.07.010.012.08784.015.756831...17.2523.08784.069.18567998.532138-999.018.049.095.0782.0
20174344.03.5082871.7101571.02.04.05.06.04344.015.602210...17.2523.04344.063.186464120.349817-999.018.042.084.0684.0

10 rows × 32 columns

bj.groupby('Month').describe()
YearDay...HourValue
countmeanstdmin25%50%75%maxcountmean...75%maxcountmeanstdmin25%50%75%max
Month
16696.02013.0000002.5821822009.02011.002013.02015.002017.06696.016.000000...17.2523.06696.0-57.093489412.008808-999.013.0042.0135.0994.0
26096.02013.0078742.5796462009.02011.002013.02015.002017.06096.014.614173...17.2523.06096.018.046096314.172648-999.014.0056.0138.0980.0
36696.02013.0000002.5821822009.02011.002013.02015.002017.06696.016.000000...17.2523.06696.047.737007228.207362-999.017.0061.0126.0784.0
47017.02012.6173582.8150312008.02010.002013.02015.002017.07017.015.791364...18.0023.07017.038.818156216.531285-999.027.0063.0111.0722.0
57440.02012.5000002.8724742008.02010.002012.52015.002017.07440.016.000000...17.2523.07440.025.278629234.123285-999.030.7558.099.0684.0
..................................................................
86696.02012.0000002.5821822008.02010.002012.02014.002016.06696.016.000000...17.2523.06696.0-4.060783280.769261-999.023.0056.097.0360.0
96480.02012.0000002.5821882008.02010.002012.02014.002016.06480.015.500000...17.2523.06480.024.521914239.468226-999.021.0056.0109.0455.0
106696.02012.0000002.5821822008.02010.002012.02014.002016.06696.016.000000...17.2523.06696.073.224014205.732168-999.022.0065.0151.0562.0
115894.02012.3976932.3625212008.02010.002012.02014.002016.05894.015.222939...17.0023.05894.080.358331215.824537-999.023.0074.0166.0666.0
125952.02012.5000002.2914802009.02010.752012.52014.252016.05952.016.000000...17.2523.05952.054.884913274.648725-999.017.0059.0173.0634.0

12 rows × 32 columns

bj.pivot_table(index = 'Year',
           columns = 'Month', values = 'Value', aggfunc = max)
Month123456789101112
Year
2008NaNNaNNaN610.0405.0270.0272.0195.0226.0415.0214.0NaN
2009-999.0178.0390.0327.0266.0712.0551.0334.0326.0361.0590.0479.0
2010485.0980.0784.0389.0314.0252.0310.0360.0455.0534.0569.0615.0
2011286.0595.0416.0279.0264.0459.0390.0341.0386.0562.0404.0522.0
2012994.0380.0407.0312.0312.0338.0318.0302.0238.0446.0445.0380.0
2013886.0532.0541.0275.0320.0466.0216.0208.0298.0407.0394.0480.0
2014671.0649.0465.0580.0271.0225.0303.0209.0229.0472.0522.0444.0
2015568.0407.0318.0722.0261.0258.0151.0141.0206.0356.0666.0634.0
2016582.0601.0427.0377.0439.0782.0252.0138.0244.0289.0378.0543.0
2017596.0386.0281.0230.0684.0130.0NaNNaNNaNNaNNaNNaN

2.北京PM2.5变化趋势分析(实战总结)

基本的数据准备

# 读入原始数据并建立索引
from datetime import datetime
bj = bj.iloc[:, [1, 6]]
bj.columns = ['datelst', 'value']
bj.set_index(pd.to_datetime(bj.datelst), inplace = True)
bj
datelstvalue
datelst
2008-04-08 15:00:002008-04-08 15:00207
2008-04-08 16:00:002008-04-08 16:00180
2008-04-08 17:00:002008-04-08 17:00152
2008-04-08 18:00:002008-04-08 18:00162
2008-04-08 19:00:002008-04-08 19:00171
.........
2017-06-30 19:00:006/30/2017 19:0051
2017-06-30 20:00:006/30/2017 20:0068
2017-06-30 21:00:006/30/2017 21:0061
2017-06-30 22:00:006/30/2017 22:0049
2017-06-30 23:00:006/30/2017 23:0055

79559 rows × 2 columns

# 缺失值处理
bj = bj[bj.value > 0]
bj
datelstvalue
datelst
2008-04-08 15:00:002008-04-08 15:00207
2008-04-08 16:00:002008-04-08 16:00180
2008-04-08 17:00:002008-04-08 17:00152
2008-04-08 18:00:002008-04-08 18:00162
2008-04-08 19:00:002008-04-08 19:00171
.........
2017-06-30 19:00:006/30/2017 19:0051
2017-06-30 20:00:006/30/2017 20:0068
2017-06-30 21:00:006/30/2017 21:0061
2017-06-30 22:00:006/30/2017 22:0049
2017-06-30 23:00:006/30/2017 23:0055

75058 rows × 2 columns

bj.index.date#只取到年月日
array([datetime.date(2008, 4, 8), datetime.date(2008, 4, 8),
       datetime.date(2008, 4, 8), ..., datetime.date(2017, 6, 30),
       datetime.date(2017, 6, 30), datetime.date(2017, 6, 30)],
      dtype=object)
# 取每日最大值作为当日PM代表
bjana = bj.groupby(bj.index.date).agg(max)
type(bjana.index)
pandas.core.indexes.base.Index
# 将索引重建为DatetimeIndex格式
bjana.index = pd.to_datetime(bjana.index)
type(bjana.index)
pandas.core.indexes.datetimes.DatetimeIndex

考察数据的基本分布特征

数据的基本分布
# PM数值的整体分布
%matplotlib inline
bjana.value.plot.hist(bins = 20)

在这里插入图片描述

# 检查逐月数据缺失情况
pd.crosstab(index=bjana.index.year, columns=bjana.index.month)
#cross出有数值的个数
col_0123456789101112
row_0
20080002331303031303160
200901029302429313130312828
2010292831303128312923312931
2011302828293130312830283031
2012292931303030302830313027
2013312831303130313130313031
2014312831303130313130313031
2015312831303130313130313031
2016312931303130313130313031
2017312831303130000000

数据的基本变化规律

bjana.groupby(bjana.index.year).median().plot()

在这里插入图片描述

bjana.groupby(bjana.index.year).max().plot()

在这里插入图片描述

bjana.groupby(bjana.index.month).median().plot()

在这里插入图片描述

bjana.groupby(bjana.index.weekday).median().plot()

在这里插入图片描述

bj.groupby(bj.index.hour).median().plot()

在这里插入图片描述

对时间周期作必要的调整

为了更好地看出秋冬变化,将第二年一月和二月的数据加入

# 对年份和月份数值做调整
bjana['month2'] = bjana.index.month 
bjana.month2.replace([1, 2], [13, 14], inplace = True)
bjana['year2'] = bjana.index.year
bjana.loc[bjana.month2 > 12, 'year2'] -= 1
bjana['2010']
datelstvaluemonth2year2
2010-01-012010-01-01 23:00129132009
2010-01-022010-01-02 23:00181132009
2010-01-032010-01-03 23:00107132009
2010-01-042010-01-04 23:0058132009
2010-01-052010-01-05 23:00106132009
...............
2010-12-272010-12-27 23:00161122010
2010-12-282010-12-28 23:00106122010
2010-12-292010-12-29 23:0069122010
2010-12-302010-12-30 23:0028122010
2010-12-312010-12-31 22:0028122010

351 rows × 4 columns

提取秋冬季四个月的数据进行考察

bjana[bjana.month2 > 10].groupby(bjana.year2).value.median().plot()

在这里插入图片描述

bjana[bjana.month2 > 10].groupby(bjana.year2).value.max().plot()

在这里插入图片描述

计算爆表天数(这是分析的考虑因素之一)

bjana[bjana.value > 500].groupby(bjana.year2).value.count().plot.bar()

在这里插入图片描述

bjana.query("value > 500 and month2 > 10").\
groupby(bjana.year2).value.count().plot.bar()

#在引用的时候可以用\来换行 \ 后面不要加注释

在这里插入图片描述

bjana.query("value > 500 and month2 >= 10").\
    groupby(bjana.year2).value.count().plot.bar()
<AxesSubplot:xlabel='year2'>

在这里插入图片描述

3. 优化Pandas

除非对相应的优化手段已经非常熟悉,否则代码的可读性应当被放在首位。

pandas为了易用性,确实牺牲了一些效率,但同时也预留了相应的优化路径。因此如果要进行优化,熟悉并优先使用pandas自身提供的优化套路至关重要。

尽量使用pandas(或者numpy)内置的函数进行运算,一般效率都会更高。

在可以用几种内部函数实现相同需求时,最好进行计算效率的比较,差距可能很大。
pandas官方提供的讨论如何进行优化的文档:https://pandas.pydata.org/pandas-docs/stable/user_guide/enhancingperf.html

学会使用各种计时工具

%time(计算紧跟在后面程序的时间)和%timeit(自动计算多次的出平均时间)%%timeit后面一段程序的平均时间

需要在IPython下才可以使用

%time bj["tmp"] = bj.Month + 10 # 运行当前代码所需时间
#(在此之前重新运行读入bj的代码)
Wall time: 1.96 ms
%timeit bj["tmp"] = bj.Month + 10
484 µs ± 16.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%%timeit    # 运行整个程序段所需平均时间

bj["tmp"] = bj.Month + 10
bj["tmp"] = bj.Month + 10
bj["tmp"] = bj.Month + 10
bj["tmp"] = bj.Month + 10
2.31 ms ± 183 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
import datetime
import time

time0 = datetime.datetime.now()

bj["tmp"] = bj.Month + 10

print(str(datetime.datetime.now() - time0))
#固定程序,学会套用即可,计算程序运行时间
0:00:00.002955

超大数据文件的处理

超大数据文件在使用pandas进行处理时可能需要考虑两个问题:读取速度,内存用量。

往往会考虑读入部分数据进行代码编写和调试,然后再对完整数据进行处理。 在数据读入和处理时需要加快处理速度,减少资源占用。

一些基本原则:

1.当明确知道数据列的取值范围时,读取数据时可以使用dtype参数来手动指定类型,如np.uint8或者np.int16,否则默认的np.int64类型等内存开销明显非常大。

2.尽量少用类型模糊的object,改用更明确的category等(用astype()转换)。

3.对类别取值较少,但案例数极多的变量,读入后尽量转换为数值代码进行处理。

data = pd.DataFrame({"a" : [0,1, 2, 3, 4, 5, 6, 7, 8, 9], 
                     "b" : ["员工编号","员工编号","领导编号","领导编号",
                            "员工编号","员工编号","员工编号","领导编号",
                            "领导编号","员工编号"]})
print(data)
data.info()
   a     b
0  0  员工编号
1  1  员工编号
2  2  领导编号
3  3  领导编号
4  4  员工编号
5  5  员工编号
6  6  员工编号
7  7  领导编号
8  8  领导编号
9  9  员工编号
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 2 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   a       10 non-null     int64 
 1   b       10 non-null     object
dtypes: int64(1), object(1)
memory usage: 288.0+ bytes
data['a'] = pd.to_numeric(data['a'], downcast = 'integer')
data['b'] = data['b'].astype('category')
print(data)
   a     b
0  0  员工编号
1  1  员工编号
2  2  领导编号
3  3  领导编号
4  4  员工编号
5  5  员工编号
6  6  员工编号
7  7  领导编号
8  8  领导编号
9  9  员工编号
data.b.head() # category格式明显更节省内存
0    员工编号
1    员工编号
2    领导编号
3    领导编号
4    员工编号
Name: b, dtype: category
Categories (2, object): ['员工编号', '领导编号']
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 2 columns):
 #   Column  Non-Null Count  Dtype   
---  ------  --------------  -----   
 0   a       10 non-null     int8    
 1   b       10 non-null     category
dtypes: category(1), int8(1)
memory usage: 244.0 bytes
data.b.astype('str') # 必要时category也可以转换回str
0    员工编号
1    员工编号
2    领导编号
3    领导编号
4    员工编号
5    员工编号
6    员工编号
7    领导编号
8    领导编号
9    员工编号
Name: b, dtype: object

对文件进行分段读取

使用chunksize参数
filename = "pm25/Beijing_2008_HourlyPM2.5_created20140325.csv"
dftmp = pd.read_csv(filename, header = 2, 
                    usecols = [0,2,3,4,5,6,7], chunksize = 5)

type(dftmp) # 注意得到的并不是一个数据框,而是TextFileReader
pandas.io.parsers.TextFileReader
cnt = 0
for item in dftmp: # 注意重复运行之后的效果
    print(item)
    cnt += 1
    if cnt > 2:
        break
      Site        Date (LST)  Year  Month  Day  Hour  Value
0  Beijing  2008-04-08 15:00  2008      4    8    15    207
1  Beijing  2008-04-08 16:00  2008      4    8    16    180
2  Beijing  2008-04-08 17:00  2008      4    8    17    152
3  Beijing  2008-04-08 18:00  2008      4    8    18    162
4  Beijing  2008-04-08 19:00  2008      4    8    19    171
      Site        Date (LST)  Year  Month  Day  Hour  Value
5  Beijing  2008-04-08 20:00  2008      4    8    20    219
6  Beijing  2008-04-08 21:00  2008      4    8    21     86
7  Beijing  2008-04-08 22:00  2008      4    8    22     63
8  Beijing  2008-04-08 23:00  2008      4    8    23     61
9  Beijing  2008-04-09 00:00  2008      4    9     0     64
       Site        Date (LST)  Year  Month  Day  Hour  Value
10  Beijing  2008-04-09 01:00  2008      4    9     1     64
11  Beijing  2008-04-09 02:00  2008      4    9     2     69
12  Beijing  2008-04-09 03:00  2008      4    9     3     80
13  Beijing  2008-04-09 04:00  2008      4    9     4     71
14  Beijing  2008-04-09 05:00  2008      4    9     5     73
使用iterator参数和get_chunk()组合
dfiter = pd.read_csv(filename, header = 2, 
                     usecols = [0,2,3,4,5,6,7], iterator = True)

type(dfiter) # 注意得到的并不是一个数据框,而是TextFileReader
pandas.io.parsers.TextFileReader
cnt = 0
for item in dfiter: # 注意结果是否正确
    print(item)
    cnt += 1
    if cnt > 2:
        break
         Site        Date (LST)  Year  Month  Day  Hour  Value
0     Beijing  2008-04-08 15:00  2008      4    8    15    207
1     Beijing  2008-04-08 16:00  2008      4    8    16    180
2     Beijing  2008-04-08 17:00  2008      4    8    17    152
3     Beijing  2008-04-08 18:00  2008      4    8    18    162
4     Beijing  2008-04-08 19:00  2008      4    8    19    171
...       ...               ...   ...    ...  ...   ...    ...
5082  Beijing  2008-11-06 09:00  2008     11    6     9     42
5083  Beijing  2008-11-06 10:00  2008     11    6    10     46
5084  Beijing  2008-11-06 11:00  2008     11    6    11     40
5085  Beijing  2008-11-06 12:00  2008     11    6    12     35
5086  Beijing  2008-11-06 13:00  2008     11    6    13     19

[5087 rows x 7 columns]
# TextFileReader使用完毕后已经失效,需要重新建立
dfiter.get_chunk(10) # 注意重复运行之后的效果
SiteDate (LST)YearMonthDayHourValue
0Beijing2008-04-08 15:0020084815207
1Beijing2008-04-08 16:0020084816180
2Beijing2008-04-08 17:0020084817152
3Beijing2008-04-08 18:0020084818162
4Beijing2008-04-08 19:0020084819171
5Beijing2008-04-08 20:0020084820219
6Beijing2008-04-08 21:002008482186
7Beijing2008-04-08 22:002008482263
8Beijing2008-04-08 23:002008482361
9Beijing2008-04-09 00:00200849064

使用modin包

modin使用并行方式对pandas中的文件读取和常见操作进行执行。

windows环境下只能使用dask引擎,其余环境下还可以使用ray
目前还不支持pandas 1.00及以上版本,只支持到0.25版本
pip install modin[dask]

非必要不要安装,可能会导致pandas降级
filename = "pm25/Beijing_2008_HourlyPM2.5_created20140325.csv"
%timeit dftmp = pd.read_csv(filename, header = 2, usecols = [0,2,3,4,5,6,7,9,10])
8.73 ms ± 113 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
import modin.pandas as pd # 只需要在这里进行修改即可

%timeit dftmp = pd.read_csv(filename, header = 2, usecols = [0,2,3,4,5,6,7,9,10])

如何优化pandas的代码

尽量不要在pandas中使用循环(行/列/单元格遍历)!!!

如果循环很难避免,尽量在循环体中使用numpy做计算。

%%time

# 标准的行/列遍历循环方式效率最差

bj['new'] = 0
for i in range(bj.shape[0]):
    bj.iloc[i, 9] = bj.iloc[i, 3] + 10
Wall time: 25.6 s
# apply自定义函数/外部函数效率稍差
def m_add(x):
    return x + 10
    
%timeit bj["new"] = bj.Month.apply(lambda x : m_add(x))
30.2 ms ± 1.83 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 在apply中应用内置函数方式多数情况下速度更快
%timeit bj["new"] = bj.Month.apply(lambda x : x + 10)
24.9 ms ± 2.44 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 直接应用原生内置函数方式速度最快(此即所谓的矢量化计算)
%timeit bj["new"] = bj.Month + 10
570 µs ± 24.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

如何进行多列数据的计算

同时涉及多列数据的计算不仅需要考虑速度优化问题,还需要考虑代码简洁性的问题。

利用lambda函数完成

%%timeit

def m_add(a, b, c):
    return a + b + c

bj['new'] = bj.apply(lambda x : 
                     m_add(x['Month'], x['Day'], x['Hour']), axis = 1)
1.07 s ± 33.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%%timeit

# lambda多参方式,因为x[0]等需要逐行定位,也浪费时间

bj['new'] = bj[['Month', 'Day', 'Hour']].apply(lambda x : 
                                               x[0] + x[1] + x[2], 
                                               axis = 1)
821 ms ± 68.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
# 还是原生方式快
%timeit bj['new'] = bj['Month'] + bj['Day'] + bj['Hour']
895 µs ± 18 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
pd.eval()命令

pd.eval()的功能仍是给表达式估值,但是基于pandas时,就可以直接利用列名等信息用于计算。

# eval()默认会使用numexpr而不是python计算引擎,复杂计算时效率很高
# engine参数在调用numexpr失败时会切换为python引擎,一般不用干涉
%timeit bj.eval('new = Month + Day + Hour', inplace = True) 
3 ms ± 125 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# eval()默认会使用numexpr而不是python计算引擎,复杂计算时效率很高
%timeit bj.eval('new = Month + Day + Hour', engine = 'python', inplace = True) 
3.04 ms ± 33.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

pd.在eval()表达式中进一步使用局部变量

@varname:在表达式中使用名称为varname的Python局部变量。

该@符号在query()和eval()中均可使用。

varx = 3
bj.eval('new = Month + Day + Hour + @varx').head() 
SiteDate (LST)YearMonthDayHourValueDurationQC Nametmpnew
0Beijing2008-04-08 15:00200848152071 HrValid1430
1Beijing2008-04-08 16:00200848161801 HrValid1431
2Beijing2008-04-08 17:00200848171521 HrValid1432
3Beijing2008-04-08 18:00200848181621 HrValid1433
4Beijing2008-04-08 19:00200848191711 HrValid1434

利用各种pandas加速包

大部分包都是基于linux环境,windows下能用的不多。

大部分包还处于测试阶段,功能上并未完善

因为外包可能会有代码兼容性的问题,非必要尽量使用内置函数
numba

对编写的自定义函数进行编译,以提高运行效率。

A Just-In-Time Compiler for Numerical Functions in Python
具体使用的是C++编写的LLVM(Low Level Virtual Machine) compiler
实际上主要是针对numpy库进行优化编译,并不仅限于pandas

import random

def monte_carlo_pi(nsamples):
    acc = 0
    for i in range(nsamples):
        x = random.random()
        y = random.random()
        if (x ** 2 + y ** 2) < 1.0:
            acc += 1
    return 4.0 * acc / nsamples

%timeit monte_carlo_pi(20) # 运行被监控的函数,获取各部分占用的运行时间数据
11.6 µs ± 380 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
from numba import jit

@jit(nopython = True) # nopython模式下性能最好
def monte_carlo_pi(nsamples):
    acc = 0
    for i in range(nsamples):
        x = random.random()
        y = random.random()
        if (x ** 2 + y ** 2) < 1.0:
            acc += 1
    return 4.0 * acc / nsamples

%timeit monte_carlo_pi(20)
The slowest run took 11.33 times longer than the fastest. This could mean that an intermediate result is being cached.
914 ns ± 1.04 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
from numba import jit

@jit
def monte_carlo_pi(nsamples):
    acc = 0
    for i in range(nsamples):
        x = random.random()
        y = random.random()
        if (x ** 2 + y ** 2) < 1.0:
            acc += 1
    return 4.0 * acc / nsamples

%timeit monte_carlo_pi(20)
443 ns ± 15.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
swifter

对apply函数进行并行操作。

是专门针对pandas进行优化的工具包

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值