山顶会课程概述¶
- 【数分+开发为主题】,迎合多维度接单
- 课程排期问题
- 课程大致内容
数据分析基本概述¶
什么是数据分析?¶
所谓的数据分析就是使用一些有效的方法和工具对收集到的数据进行处理,从中发现数据的关键趋势或者规律,以便做出合理的决策和提出有针对性的建议。通俗来说,数据分析就是从数据中找到有用的信息来帮助我们做出更明智、更准确的决策。
总之,数据分析是将数据转化为可理解的信息和见解,可为业务提供决策信息、帮助解决问题和提高效率的过程。本质上,所有的决策、战略和规划都需要数据驱动,数据分析在这个过程中起到了突出的作用。
简单一句话描述数据分析:数据分析可以实现数据价值的最大化!
数据分析的技术实现¶
不写代码的实现¶
处理简单或者普通难度的业务逻辑的分析处理
- Excel
- Mysql
- BI工具(PowerBI或者Tableau)
写代码的实现¶
处理普通难度的业务逻辑的分析处理+复杂的业务逻辑处理
- Python
- 数据分析三剑客:Numpy、Pandas和Matplotlib
- 开发环境安装:
- 链接: https://pan.baidu.com/s/1xI-RafNRZKDQPI7WMSmd2A?pwd=6x2v 提取码: 6x2v
- anaconda:数据分析的集成环境(包含了各种数据分析的模块)
Numpy¶
NumPy(Numerical Python)是Python中用于数据分析、机器学习、科学计算的重要工具包,也是python进行科学计算重要基础库之一,多数值运算。
Pandas(重点)¶
Pandas 库是一个免费、开源的第三方 Python 库,是 Python 数据分析和机器学习必不可少的工具之一,它为 Python 数据分析提供了高性能,且易于使用的数据结构,即 Series 和 DataFrame。Pandas 自诞生后被应用于众多的领域,比如金融、统计学、社会科学、建筑工程等。
Pandas 库基于 Python NumPy 库开发而来,因此,它可以与 Python 的科学计算库配合使用。Pandas 提供了两种数据结构,分别是 Series(一维数组结构)与 DataFrame(二维表格结构),这两种数据结构极大地增强的了 Pandas 的数据分析能力。
数据结构介绍:数据存储在不同的数据结构表示的容器中,则可以基于容器的特性对数据进行不同维度的运算处理操作。
Matplotlib¶
matplotlib是一个用于创建可视化图表的Python库。它提供了一组广泛的功能,用于绘制线图、散点图、柱状图、饼图、等高线图、热图等各种类型的图表。
matplotlib是一个功能强大且灵活的库,被广泛应用于数据可视化、科学计算、工程绘图等领域。
图表绘制在数据分析中主要用户进行数据探索和分析结果的展示。
pandas数据分析库¶
Pandas 提供了两种数据结构,分别是 Series(一维数组结构)与 DataFrame(二维数组结构),这两种数据结构极大地增强的了 Pandas 的数据分析能力。
Series¶
概述¶
Series是一种类似与一维数组的对象,由下面两个部分组成:
- values:一组数据
- index:相关的数据索引标签
常见操作¶
In [60]:- import pandas as pd
- from pandas import Series,DataFrame
复制代码 In [64]:- s1 = Series(data=[3,3,6,6,8,8,9,9])
- s1
复制代码 Out[64]:- 0 3
- 1 3
- 2 6
- 3 6
- 4 8
- 5 8
- 6 9
- 7 9
- dtype: int64
复制代码 In [62]:- s2 = Series(data={'name':'bobo','salary':10000,'age':30})
- s2
复制代码 Out[62]:- name bobo
- salary 10000
- age 30
- dtype: object
复制代码
- Series的索引
- 隐式索引:默认形式的索引(0,1,2....)
- 显示索引:自定义的索引,可以通过index参数设置显示索引
In [66]: Out[66]: In [69]: Out[69]:
In [73]: Out[73]: In [74]: Out[74]:- name bobo
- salary 10000
- age 30
- dtype: object
复制代码
- Series的常用方法
- head(),tail()
- unique(),nunique()
- value_counts()
- isnull(),notnull()
In [76]:- s1.head(3),s1.tail(2) #用来显示前几个或者后几个元素
复制代码 Out[76]:- (0 3
- 1 3
- 2 6
- dtype: int64,
- 6 9
- 7 9
- dtype: int64)
复制代码 In [78]: Out[78]: In [79]: Out[79]: In [80]:- s1.value_counts() #统计元素出现的次数
复制代码 Out[80]: In [82]:- #series的运算
- s1 + 100 #让s1的每一个元素都加上100
复制代码 Out[82]:- 0 103
- 1 103
- 2 106
- 3 106
- 4 108
- 5 108
- 6 109
- 7 109
- dtype: int64
复制代码 In [83]:- s3 = Series(data=[1,2,3],index=['a','b','c'])
- s3
复制代码 Out[83]: In [84]:- s4 = Series(data=[1,2,3],index=['a','b','d'])
- s4
复制代码 Out[84]: In [85]:- s3 + s4 #NAN就是None
- #在Series的运算中,只有索引一致的元素可以进行算数运算,否则补空
复制代码 Out[85]:- a 2.0
- b 4.0
- c NaN
- d NaN
- dtype: float64
复制代码 DataFrame(重要)¶
概述¶
- DataFrame是一个【表格型】的数据结构。DataFrame由按一定顺序排列的多列数据组成。设计初衷是将Series的使用场景从一维拓展到多维。DataFrame既有行索引,也有列索引。
- 行索引:index
- 列索引:columns
- 值:values
DataFrame的创建¶
In [87]:- dic = {
- 'name':['Tom','Jerry','Jay'],
- 'age':[10,20,30],
- 'salary':[2000,3000,4000]
- }
- table = DataFrame(data=dic)
- table
复制代码 Out[87]: nameagesalary0Tom1020001Jerry2030002Jay304000 DataFrame的常用属性¶
- values,columns,index,shape
In [88]: Out[88]: In [89]: Out[89]:- array([['Tom', 10, 2000],
- ['Jerry', 20, 3000],
- ['Jay', 30, 4000]], dtype=object)
复制代码 In [90]: Out[90]:- RangeIndex(start=0, stop=3, step=1)
复制代码 In [91]: Out[91]:- Index(['name', 'age', 'salary'], dtype='object')
复制代码 索引操作(重点)¶
In [93]:- dic = {'names':['jay','tom','jerry'],
- 'salary':[1000,2000,3000],
- 'age':[30,40,50]}
- df = DataFrame(data=dic,index=['a','b','c'])
- df
复制代码 Out[93]: namessalaryageajay100030btom200040cjerry300050 In [94]: Out[94]:- a 30
- b 40
- c 50
- Name: age, dtype: int64
复制代码 In [95]:- #索引取多列:df[[col1,coln]]
- df[['age','names']]
复制代码 Out[95]: agenamesa30jayb40tomc50jerry In [96]:- #索引取单行:df.loc/iloc[index]
- df.loc['a']
复制代码 Out[96]:- names jay
- salary 1000
- age 30
- Name: a, dtype: object
复制代码 In [99]:- df.iloc[0] #iloc后面跟的是隐式索引,loc后面跟显示索引
复制代码 Out[99]:- names jay
- salary 1000
- age 30
- Name: a, dtype: object
复制代码 In [100]:- #索引取多行:df.loc/iloc[[index1,indexn]]
- df.loc[['b','a']]
复制代码 Out[100]: namessalaryagebtom200040ajay100030 In [102]: Out[102]: namessalaryageajay100030btom200040cjerry300050 In [104]:- #索引取元素
- df.loc['b','names'] #逗号左边是行,右边是列
复制代码 Out[104]: 切片操作¶
In [105]: Out[105]: namessalaryageajay100030btom200040cjerry300050 In [106]:- #切列
- df.loc[:,'names':'salary']
复制代码 Out[106]: namessalaryajay1000btom2000cjerry3000 数据查看¶
- 查看DataFrame的概览和统计信息
- head()
- tail()
- info()
- describe()
In [109]: - <class 'pandas.core.frame.DataFrame'>
- Index: 3 entries, a to c
- Data columns (total 3 columns):
- # Column Non-Null Count Dtype
- --- ------ -------------- -----
- 0 names 3 non-null object
- 1 salary 3 non-null int64
- 2 age 3 non-null int64
- dtypes: int64(2), object(1)
- memory usage: 204.0+ bytes
复制代码 In [110]:- df.describe() #对数据表格进行统计描述
复制代码 Out[110]: salaryagecount3.03.0mean2000.040.0std1000.010.0min1000.030.025%1500.035.050%2000.040.075%2500.045.0max3000.050.0 数据保存与加载¶
csv¶
In [112]:- #将df数据写入到文件中存储
- dic = {'names':['jay','tom','jerry'],
- 'salary':[1000,2000,3000],
- 'age':[30,40,50]}
- df = pd.DataFrame(data=dic,index=['a','b','c'])
- df
复制代码 Out[112]: namessalaryageajay100030btom200040cjerry300050 In [113]: In [114]:- #读取外部文件 ./data/透视表-篮球赛.csv的数据到df表格中
- ball = pd.read_csv('data/透视表-篮球赛.csv')
- ball
复制代码 Out[114]: 对手胜负主客场命中投篮数投篮命中率3分命中率篮板助攻得分0勇士胜客10230.4350.444611271国王胜客8210.3810.28639272小牛胜主10190.5260.46237293灰熊负主8200.4000.2505822476人胜客10200.5000.250313275黄蜂胜客8180.4440.4001011276灰熊负客6190.3160.2224820776人负主8210.3810.42947298尼克斯胜客9230.3910.35359319老鹰胜客8150.5330.5453112910爵士胜主19250.7600.8752135611骑士胜主8210.3810.42911133512灰熊胜主11250.4400.429483813步行者胜客9210.4290.2505152614猛龙负主8250.3200.2736113815太阳胜客12220.5450.545274816灰熊胜客9200.4500.500572917掘金胜主6160.3750.143892118尼克斯胜主12270.4440.3852103719篮网胜主13200.6500.6151083720步行者胜主8220.3640.3338102921湖人胜客13220.5910.444493622爵士胜客8190.4210.333532923开拓者胜客16290.5520.571834824鹈鹕胜主8160.5000.40011726 Excel¶
环境安装:
pip install xlrd -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install xlwt -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install openpyxl -i https://pypi.tuna.tsinghua.edu.cn/simple
- to_excel(filaPath,sheet_name) & read_excel(filaPath,sheet_name)
In [116]:- #读取excel数据:data/运营商数据.xlsx
- opt = pd.read_excel('data/运营商数据.xlsx')
- opt
复制代码 Out[116]: 用户号码用户套餐月租入网时间近6个月平均话费近6个月平均使用流量近6个月平均使用语音优惠名称号码品牌用户年龄用户性别是否订购是否参与活动活动开始时间活动结束时间外呼团队外呼时间外呼分钟数015620020209146.20509090.910500398.3167送3个月会员4G55男否NaNNaNNaNNaN2019119112502006042450.00003980.59276786.9000送3个月会员4G51男否NaNNaNNaNNaN2019092823502011120667.11251706.841767453.0833送3个月会员4G36女是会员赠送3个月201909.0202008.0团队D20190912834562012041299.00002872.30306741.3500送3个月会员4G35女是会员赠送3个月201909.0202008.0团队D2019099145882015050388.000028222.901100326.3500送3个月会员4G57男是会员赠送3个月201909.0202008.0团队D20190999......................................................1649316494492004101449.050050.79396757.2000送3个月会员4G23女是会员赠送3个月201910.0202009.0团队D20191084164941649592006031015.4250554.28600056.7667送3个月会员4G47女否NaNNaNNaNNaN20191101649516496282002041764.73500.002900111.8833送3个月会员2G61男否NaNNaNNaNNaN201910341649616497152012100118.1750186.96383321.8333送3个月会员2G28男否NaNNaNNaNNaN201910341649716498192017110336.22503839.240000149.6667送3个月会员4G37女是会员赠送3个月201910.0202009.0团队D2019106316498 rows × 17 columns
In [117]: Out[117]: In [118]:- #写入数据到excel中
- dic = {'names':['jay','tom','jerry'],
- 'salary':[1000,2000,3000],
- 'age':[30,40,50]}
- df = pd.DataFrame(data=dic,index=['a','b','c'])
- df
复制代码 Out[118]: namessalaryageajay100030btom200040cjerry300050 In [119]:
- 为什么需要将外部文件的数据读取加载到DataFrame表格中呢?
- 将外部文件读取到DataFrame中,我们就可以基于DataFrame自身的特性对数据进行不同维度的运算和处理
sql¶
环境安装:
pip install sqlalchemy -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple
- 写入数据到数据库
- from sqlalchemy import create_engine
- 创建链接对象:
- conn = create_engine('mysql+pymysql://root:boboadmin@127.0.0.1:3306/spider?charset=UTF8MB4')
In [122]:- dic = {'names':['jay','tom','jerry'],
- 'salary':[1000,2000,3000],
- 'age':[30,40,50]}
- df = pd.DataFrame(data=dic)
- df
复制代码 Out[122]: namessalaryage0jay1000301tom2000402jerry300050 In [125]:- from sqlalchemy import create_engine
- #创建一个链接对象
- #mysql+pymysql://用户名:密码@ip:port/dbName?charset=UTF8MB4
- conn = create_engine('mysql+pymysql://root:boboadmin@127.0.0.1:3306/new_spider?charset=UTF8MB4')
- df.to_sql(name='tb_df_new',con=conn)
复制代码 Out[125]:
In [126]:- import pymysql
- conn = pymysql.Connect(
- host = '127.0.0.1', #数据库服务器地址
- port = 3306, #数据库端口
- user = 'root', #数据库的用户名
- password = 'boboadmin', #密码
- db = 'new_spider' #数据库名字
- )
- ret = pd.read_sql('select * from dep',conn)
- ret
复制代码 - /Users/zhangxiaobo/opt/arm-anaconda/anaconda3/lib/python3.9/site-packages/pandas/io/sql.py:761: UserWarning: pandas only support SQLAlchemy connectable(engine/connection) ordatabase string URI or sqlite3 DBAPI2 connectionother DBAPI2 objects are not tested, please consider using SQLAlchemy
- warnings.warn(
复制代码 Out[126]: idname0200技术1201人力资源2202销售3203运营 股票分析案例¶
In [186]:- import pandas as pd
- #可以将本地的文件数据读取到df,./data/600519.xlsx
- df = pd.read_excel('data/600519.xlsx')
- df.head()
复制代码 Out[186]: Unnamed: 0dateopenclosehighlowvolumecode002015-01-0524.09635.82337.38723.25094515.0600519112015-01-0633.53231.56035.86029.91455020.0600519222015-01-0729.93227.11433.07824.43254797.0600519332015-01-0828.07826.04128.55024.56940525.0600519442015-01-0924.80524.72329.68724.54153982.0600519 In [187]:- #删除无用的一列
- df.drop(columns='Unnamed: 0',inplace=True) #inplace=True将删除操作作用在了原始数据中
复制代码 In [188]: Out[188]: In [189]: Out[189]: dateopenclosehighlowvolumecode02015-01-0524.09635.82337.38723.25094515.060051912015-01-0633.53231.56035.86029.91455020.060051922015-01-0729.93227.11433.07824.43254797.060051932015-01-0828.07826.04128.55024.56940525.060051942015-01-0924.80524.72329.68724.54153982.0600519 In [190]: - <class 'pandas.core.frame.DataFrame'>
- RangeIndex: 2153 entries, 0 to 2152
- Data columns (total 7 columns):
- # Column Non-Null Count Dtype
- --- ------ -------------- -----
- 0 date 2153 non-null object
- 1 open 2153 non-null float64
- 2 close 2153 non-null float64
- 3 high 2153 non-null float64
- 4 low 2153 non-null float64
- 5 volume 2153 non-null float64
- 6 code 2153 non-null int64
- dtypes: float64(5), int64(1), object(1)
- memory usage: 117.9+ KB
复制代码 In [191]:- #将date列转转换成时间类型
- df['date'] = df['date'].astype('datetime64') #astype用作类型转换
复制代码 In [192]: - <class 'pandas.core.frame.DataFrame'>
- RangeIndex: 2153 entries, 0 to 2152
- Data columns (total 7 columns):
- # Column Non-Null Count Dtype
- --- ------ -------------- -----
- 0 date 2153 non-null datetime64[ns]
- 1 open 2153 non-null float64
- 2 close 2153 non-null float64
- 3 high 2153 non-null float64
- 4 low 2153 non-null float64
- 5 volume 2153 non-null float64
- 6 code 2153 non-null int64
- dtypes: datetime64[ns](1), float64(5), int64(1)
- memory usage: 117.9 KB
复制代码 In [193]:- #显示索引优势:可以增加数据的可读性
- #将date列作为表格的行索引
- df.set_index('date',inplace=True)
复制代码 In [194]: Out[194]: openclosehighlowvolumecodedate 2015-01-0524.09635.82337.38723.25094515.06005192015-01-0633.53231.56035.86029.91455020.06005192015-01-0729.93227.11433.07824.43254797.06005192015-01-0828.07826.04128.55024.56940525.06005192015-01-0924.80524.72329.68724.54153982.0600519 In [195]: Out[195]:
- 计算股票的每日收益率和7日波动率:通过计算收益率和波动率,我们可以评估股票的风险和收益情况。
- 每日收益率:(当日收盘价 - 前一日的收盘价)/ 前一日的收盘价
- 7日波动率:对每日收益率数据进行每7日滚动的方差计算
In [196]:- df['close'].shift(1) #前日收盘
复制代码 Out[196]:- date
- 2015-01-05 NaN
- 2015-01-06 35.823
- 2015-01-07 31.560
- 2015-01-08 27.114
- 2015-01-09 26.041
- ...
- 2023-11-03 1779.500
- 2023-11-06 1811.240
- 2023-11-07 1812.000
- 2023-11-08 1791.170
- 2023-11-09 1798.340
- Name: close, Length: 2153, dtype: float64
复制代码 In [197]:- #每日收益率:(当日收盘价 - 前一日的收盘价)/ 前一日的收盘价
- day_rate = (df['close'] - df['close'].shift(1)) / df['close'].shift(1)
- day_rate
复制代码 Out[197]:- date
- 2015-01-05 NaN
- 2015-01-06 -0.119002
- 2015-01-07 -0.140875
- 2015-01-08 -0.039574
- 2015-01-09 -0.050612
- ...
- 2023-11-03 0.017836
- 2023-11-06 0.000420
- 2023-11-07 -0.011496
- 2023-11-08 0.004003
- 2023-11-09 -0.002352
- Name: close, Length: 2153, dtype: float64
复制代码 In [198]:- #7日波动率
- day_7_rolling_rate = day_rate.rolling(7).var() #var计算一组数据的方差
- day_7_rolling_rate
复制代码 Out[198]:- date
- 2015-01-05 NaN
- 2015-01-06 NaN
- 2015-01-07 NaN
- 2015-01-08 NaN
- 2015-01-09 NaN
- ...
- 2023-11-03 0.000457
- 2023-11-06 0.000442
- 2023-11-07 0.000513
- 2023-11-08 0.000510
- 2023-11-09 0.000525
- Name: close, Length: 2153, dtype: float64
复制代码
- 查找股票的市值最大和最小日
- 市值 = 收盘价 * 成交量
- 找出市值数据中最大最小值下标(市值最大和最小日期)
In [199]:- #每日市值
- day_values = df['close'] * df['volume']
- day_values
复制代码 Out[199]:- date
- 2015-01-05 3.385811e+06
- 2015-01-06 1.736431e+06
- 2015-01-07 1.485766e+06
- 2015-01-08 1.055312e+06
- 2015-01-09 1.334597e+06
- ...
- 2023-11-03 5.465598e+07
- 2023-11-06 4.624949e+07
- 2023-11-07 3.507469e+07
- 2023-11-08 2.615865e+07
- 2023-11-09 2.296461e+07
- Length: 2153, dtype: float64
复制代码 In [200]:- #找出市值数据中最大最小值下标(市值最大和最小日期)
- day_values.idxmax() #求最大元素下标
复制代码 Out[200]:- Timestamp('2021-09-27 00:00:00')
复制代码 In [201]:- day_values.idxmin() #求最小元素下标
复制代码 Out[201]:- Timestamp('2015-02-02 00:00:00')
复制代码
In [202]:- ex = (df['close'] - df['open']) / df['open'] > 0.03
- ex #想获取所有True对应的索引
复制代码 Out[202]:- date
- 2015-01-05 True
- 2015-01-06 False
- 2015-01-07 False
- 2015-01-08 False
- 2015-01-09 False
- ...
- 2023-11-03 False
- 2023-11-06 False
- 2023-11-07 False
- 2023-11-08 False
- 2023-11-09 False
- Length: 2153, dtype: bool
复制代码 In [203]:- #在DataFrame中,可以使用布尔值作为表格的行索引:就会保留True对应的行数据,忽略False对应的行数据
- df.loc[ex] #取出了True对应的行数据(满足要求的行数据)
复制代码 Out[203]: openclosehighlowvolumecodedate 2015-01-0524.09635.82337.38723.25094515.06005192015-01-1518.88720.86921.16917.60548585.06005192015-01-2011.73213.60515.8058.98761022.06005192015-01-2113.77817.49617.98712.80552674.06005192015-01-2315.46016.27818.33215.45033084.0600519.....................2022-11-151484.1791540.1791551.1491473.17956318.06005192023-01-051711.0891775.0891775.0891707.08947943.06005192023-02-201795.0891849.0891852.8891791.28929669.06005192023-05-221664.0991720.0891726.0891664.08941284.06005192023-07-281832.0001897.0001900.0001828.01039018.0600519252 rows × 6 columns
In [204]:- df.loc[ex].index #获取了满足要求行数据的行索引
复制代码 Out[204]:- DatetimeIndex(['2015-01-05', '2015-01-15', '2015-01-20', '2015-01-21',
- '2015-01-23', '2015-01-26', '2015-02-03', '2015-02-09',
- '2015-02-11', '2015-02-16',
- ...
- '2022-06-10', '2022-06-17', '2022-08-31', '2022-11-01',
- '2022-11-04', '2022-11-15', '2023-01-05', '2023-02-20',
- '2023-05-22', '2023-07-28'],
- dtype='datetime64[ns]', name='date', length=252, freq=None)
复制代码
- 假如张三从2015年1月1日开始,每月第一个交易日买入1手股票,每年最后一个交易日卖出所有股票,到今天为止,我的收益如何?
- 分析:
- 买入股票
- 一个完整的年,需要买入12手1200支股票。以购买当期的开盘价进行股票的买卖。
- 卖出股票
- 一个完整的年,需要卖出1200支股票(收盘价为单价)
- 特殊情况:
- 最后一年就是一个特殊的年(因为没有到该年最后一个交易日),只可以买不可以卖,但是手里剩余的股票是需要计算到总收益中。
- resample函数介绍:pandas库中的resample函数主要用于将时间序列数据重新采样到不同的时间频率,例如从按天采样重新采样为按周或按月采样。resample函数的常用语法如下:
- df.resample(rule, ...).func()
- 其中,df是一个时间序列数据的DataFrame,rule是指定重采样频率的规则字符串(H小时、W星期、M月、A年等),func是用于聚合数据的函数(例如求和、平均值等)。例如:
- df.resample('H').mean()
- df.resample('W').sum()
- df.resample('M').max()
In [215]:- #找出每个月的第一个交易日的开盘价
- monthly = df.resample('M').first() #取出了每个月第一个交易日对应的行数据
- monthly.head(5)
- #获取的数据会发现日期是每月最后一天的日期并不是第一个交易日的日期?(无需解决,自身存在的bug),但是行数据是没错
复制代码 Out[215]: openclosehighlowvolumecodedate 2015-01-3124.09635.82337.38723.25094515.06005192015-02-2811.26910.64112.0789.44133983.06005192015-03-3125.16925.10527.89623.53231098.06005192015-04-3029.92329.29631.31428.51476875.06005192015-05-3181.40583.50587.16979.00554739.0600519 In [216]:- #买入股票的总花费
- total_cost = monthly['open'].sum() * 100
- total_cost
复制代码 Out[216]:
In [219]:- yearly = df.resample('A').last() #A表示年
- yearly
复制代码 Out[219]: openclosehighlowvolumecodedate 2015-12-3173.91073.88075.19073.51019673.06005192016-12-31188.471196.011197.151188.47134687.06005192017-12-31586.648566.138595.148560.24876038.06005192018-12-31442.947469.657476.047439.64763678.06005192019-12-311077.1861077.1861082.1861070.69622588.06005192020-12-311852.2111909.2111910.1911850.21138860.06005192021-12-312000.5041980.5042003.4841958.50429665.06005192022-12-311710.0891701.0891727.0791701.08925333.06005192023-12-311790.1101794.1101799.0001783.00012800.0600519 In [220]:- recv = yearly['close'].sum() * 1200
- recv
复制代码 Out[220]: In [221]: Out[221]: 数据清洗¶
概述¶
数据清洗是指对原始数据进行处理和转换,以去除无效、重复、缺失或错误的数据,使数据符合分析的要求。
作用和意义¶
- 提高数据质量:
- 通过数据清洗,数据质量得到提升,减少错误分析和错误决策。
- 增加数据可用性:
- 清洗后的数据更加规整和易于使用,提高数据的可用性和可读性。
清洗维度¶
- 缺失值处理:
- 对于缺失的数据,可以删除包含缺失值的行或列或者填充缺失值。
- 重复值处理:
- 识别和删除重复的数据行,避免重复数据对分析结果产生误导。
- 异常值处理:
缺失值清洗¶
缺失值/空值的删除¶
In [222]:- import pandas as pd
- from pandas import DataFrame,Series
- df = pd.read_csv('./data/none.csv',index_col=0)
- df #NAN就是None空白
复制代码 Out[222]: 01234022644.0NaN111988820.085.01621983NaN84.0463936476.0NaN85476320.021.0455361936.0NaN82653987.089.01
- 缺失值的检测和删除,相关方法:
- isnull():检测df中的每一个元素是否为空值,为空则给该元素返回True,否则返回False
- notnull():检测df中的每一个元素是否为非空值,为非空则给该元素返回True,否则返回False
- any():检测一行或一列布尔值中是否存在一个或多个True,有则返回True,否则返回False
- all():检测一行或一列布尔值中是否存全部为True,有则返回True,否则返回False
- dropna():将存在缺失值/空值的行或者列进行删除
In [223]: Out[223]: 012340FalseFalseFalseTrueFalse1FalseFalseFalseFalseFalse2FalseFalseTrueFalseFalse3FalseFalseFalseTrueFalse4FalseFalseFalseFalseFalse5FalseFalseFalseTrueFalse6FalseFalseFalseFalseFalse In [224]: Out[224]: 012340TrueTrueTrueFalseTrue1TrueTrueTrueTrueTrue2TrueTrueFalseTrueTrue3TrueTrueTrueFalseTrue4TrueTrueTrueTrueTrue5TrueTrueTrueFalseTrue6TrueTrueTrueTrueTrue In [226]:- #可以判定哪些列中存在空值
- df.isnull().any(axis=0)
- #axis=0表示针对列进行any操作
- #axis=1表示针对行进行any操作
复制代码 Out[226]:- 0 False
- 1 False
- 2 True
- 3 True
- 4 False
- dtype: bool
复制代码 In [228]: Out[228]:- 0 True
- 1 True
- 2 False
- 3 False
- 4 True
- dtype: bool
复制代码
In [240]:- df.dropna() #直接返回删除空值对应行后的结果,不会直接改变原始数据
复制代码 Out[240]: 012341988820.085.016476320.021.045653987.089.01
In [238]:- for col in df.columns:
- #满足该条件则表示第col列中是存在空值
- if df[col].isnull().sum() > 0:
- #求出了该列空值的个数
- null_count = df[col].isnull().sum()
- #求出该列中空值的占比:空值的数量/列的总元素个数
- p = format(null_count / df[col].size,'.2%')
- print(col,null_count,p)
复制代码
- 缺失值/空值的填充
- fillna(value,method,axis)
- 参数介绍:
- value:给空值填充的值
- method:填充方式,可以为bfill向后填充和ffill向前填充
- axis:填充轴向
In [245]: Out[245]: 01234022644.0666.0111988820.085.01621983666.084.0463936476.0666.085476320.021.0455361936.0666.082653987.089.01
In [249]:- df.fillna(axis=0,method='ffill').fillna(axis=0,method='bfill') #bfill
- #在竖直方向上,会用空前面的值填充空值
复制代码 Out[249]: 01234022644.085.0111988820.085.0162198320.084.0463936476.084.085476320.021.0455361936.021.082653987.089.01
In [251]:- #可以使用空值列的均值、中位数等统计指标对空值进行填充
- for col in df.columns:
- if df[col].isnull().sum() > 0:
- #计算出空值列对应的均值
- mean_value = df[col].mean()
- df[col].fillna(value=mean_value,inplace=True)
复制代码 In [252]: Out[252]: 01234022644.00000069.75111988820.00000085.00162198333.83333384.00463936476.00000069.7585476320.00000021.00455361936.00000069.7582653987.00000089.001 注意:实现空值的清洗最好选择删除的方式,如果删除的成本比较高,再选择填充的方式。
重复值清洗¶
In [253]:- df = pd.read_csv('data/repeat.csv',index_col=0)
- df
复制代码 Out[253]: 01234076820149517085377286279692245300000456462514495000006662898141700000
In [255]: Out[255]:
- 使用drop_duplicates()方法检测且删除重复的行数据
In [256]: Out[256]: 01234076820149517085377286279692245300000456462514496662898141 异常值清洗¶
异常值是分析师和数据科学家常用的术语,因为它需要密切注意,否则可能导致错误的估计。 简单来说,异常值是一个观察值,远远超出了样本中的整体模式。
异常值在统计学上的全称是疑似异常值,也称作离群点,异常值的分析也称作离群点分析。异常值是指样本中出现的“极端值”,数据值看起来异常大或异常小,其分布明显偏离其余的观测值。异常值分析是检验数据中是否存在不合常理的数据。
- 给定条件的异常数据处理
- 自定义一个1000行3列(A,B,C)取值范围为0-1的数据源,然后将C列中的值大于其两倍标准差的异常值进行清洗
In [257]:- data = pd.read_csv('./data/outlier.csv',index_col=0)
- data
复制代码 Out[257]: ABC00.7945140.3379130.29929010.5962590.5129300.55436920.1150030.4014900.66957330.7730070.5472630.78085740.4692550.3169570.214900............9950.6501190.0425320.4051129960.7042710.3171550.7797649970.1382250.4936250.1522159980.2731300.7638460.0312429990.5366710.6748450.0042241000 rows × 3 columns
In [261]:- #C列的2倍标准差
- twice_std = data['C'].std() * 2
- twice_std
复制代码 Out[261]: In [267]:- #判定异常值
- ex = data['C'] > twice_std
- ex #True表示为异常值,False表示正常值
复制代码 Out[267]:- 0 False
- 1 False
- 2 True
- 3 True
- 4 False
- ...
- 995 False
- 996 True
- 997 False
- 998 False
- 999 False
- Name: C, Length: 1000, dtype: bool
复制代码 In [268]:- data.loc[ex] #取出了True对应的行数据(异常值对应的行数据)
复制代码 Out[268]: ABC20.1150030.4014900.66957330.7730070.5472630.78085770.0132300.4195070.96072880.8580910.8059640.586865100.1588100.0955860.775476............9810.8036460.7915880.7828599890.5342870.7349840.7013729910.2589870.0398010.7514509930.0029570.9399430.6732079960.7042710.3171550.779764424 rows × 3 columns
In [269]:- drop_indexs = data.loc[ex].index #提取了异常值对应行数据的行索引
- drop_indexs
复制代码 Out[269]:- Int64Index([ 2, 3, 7, 8, 10, 11, 13, 14, 20, 21,
- ...
- 964, 965, 972, 975, 978, 981, 989, 991, 993, 996],
- dtype='int64', length=424)
复制代码 In [270]:- #将异常值对应的行从数据表格中进行删除
- data.drop(index=drop_indexs)
复制代码 Out[270]: ABC00.7945140.3379130.29929010.5962590.5129300.55436940.4692550.3169570.21490050.5393570.1074760.18749560.3855990.5619300.377683............9940.4942480.5581010.1285419950.6501190.0425320.4051129970.1382250.4936250.1522159980.2731300.7638460.0312429990.5366710.6748450.004224576 rows × 3 columns
map映射¶
- 映射就是指给一组数据中的每一个元素绑定一个固定的数据
In [283]:- df = pd.read_csv('./data/map.csv').drop(columns='Unnamed: 0')
- df
复制代码 Out[283]: namesalary0张三100001李四150002王五210003张三10000 In [286]:- #给每个人起一个英文名,将其作为表格中新的一列存在
- dic = {
- '张三':'Tom',
- '李四':'Jerry',
- '王五':'Jay'
- }#映射关系表
- df['ename'] = df['name'].map(dic)
- df
复制代码 Out[286]: namesalaryename0张三10000Tom1李四15000Jerry2王五21000Jay3张三10000Tom map充当运算工具¶
In [289]:- #将每一个人的税后薪资进行计算:超过5000部分的钱需要缴纳25%的税
- def after_sal(s): #参数s就依次表示每一个人的薪资数据
- return s - (s-5000)*0.25
- df['after_sal'] = df['salary'].map(after_sal)
- df
复制代码 Out[289]: namesalaryenameafter_sal0张三10000Tom8750.01李四15000Jerry12500.02王五21000Jay17000.03张三10000Tom8750.0 排序¶
In [280]:- data = pd.read_csv('./data/outlier.csv',index_col=0)
- data.head()
复制代码 Out[280]: ABC00.7945140.3379130.29929010.5962590.5129300.55436920.1150030.4014900.66957330.7730070.5472630.78085740.4692550.3169570.214900 In [281]:- data.sort_values(by='C') #默认根据C列中的元素从小到大进行排序
复制代码 Out[281]: ABC6470.1028260.2688950.0000365210.4915870.7670860.0006805990.5603230.8849600.001386170.4753330.9688090.0026397170.5610990.5967510.002810............9130.5759180.1552750.995703910.9144150.7389600.9965642730.7467500.4704660.996640670.8032910.9596920.9967803290.7283170.8106220.9985171000 rows × 3 columns
In [282]:- data.sort_values(by='C',ascending=False) #从大到小排序
复制代码 Out[282]: ABC3290.7283170.8106220.998517670.8032910.9596920.9967802730.7467500.4704660.996640910.9144150.7389600.9965649130.5759180.1552750.995703............7170.5610990.5967510.002810170.4753330.9688090.0026395990.5603230.8849600.0013865210.4915870.7670860.0006806470.1028260.2688950.0000361000 rows × 3 columns
In [292]:- #axis=0表示的行,axis=1表示的是列
- data.sort_index(axis=1,ascending=False)
复制代码 Out[292]: CBA00.2992900.3379130.79451410.5543690.5129300.59625920.6695730.4014900.11500330.7808570.5472630.77300740.2149000.3169570.469255............9950.4051120.0425320.6501199960.7797640.3171550.7042719970.1522150.4936250.1382259980.0312420.7638460.2731309990.0042240.6748450.5366711000 rows × 3 columns
In [295]:- #手动对列索引进行排列,此处indices表示排列的结果(只能用隐式索引)
- #axis=0表示的行,axis=1表示的是列
- data.take(indices=[1,0,2],axis=1)
复制代码 Out[295]: BAC00.3379130.7945140.29929010.5129300.5962590.55436920.4014900.1150030.66957330.5472630.7730070.78085740.3169570.4692550.214900............9950.0425320.6501190.4051129960.3171550.7042710.7797649970.4936250.1382250.1522159980.7638460.2731300.0312429990.6748450.5366710.0042241000 rows × 3 columns
map运算¶
In [277]:- #计算下面表格中每个人的税后薪资:超过3000部分的钱缴纳50%的税,计算每个人的税后薪资
- #加载数据
- df = pd.read_csv('./data/fruits.csv').drop(columns='Unnamed: 0')
- df
复制代码 Out[277]: itempricecolorweight0Apple4.0red121Banana3.0yellow202Orange3.0yellow503Banana2.5green304Orange4.0green205Apple2.0green44 分组聚合¶
- 数据分类处理的核心:
- groupby()函数
- groups属性查看分组情况
In [296]:- #加载数据
- df = pd.read_csv('./data/fruits.csv').drop(columns='Unnamed: 0')
- df
复制代码 Out[296]: itempricecolorweight0Apple4.0red121Banana3.0yellow202Orange3.0yellow503Banana2.5green304Orange4.0green205Apple2.0green44 In [298]:- #想根据不同水果种类对数据进行分组
- df.groupby(by='item').groups #使用groupby分组后,调用groups查看分组的结果
复制代码 Out[298]:- {'Apple': [0, 5], 'Banana': [1, 3], 'Orange': [2, 4]}
复制代码 In [300]:- #计算不同水果的平均价格
- df.groupby(by='item')['price'] #单独取出每组数据的价格数据
- mean_price = df.groupby(by='item')['price'].mean()
- mean_price
复制代码 Out[300]:- item
- Apple 3.00
- Banana 2.75
- Orange 3.50
- Name: price, dtype: float64
复制代码 In [304]: Out[304]:- {'Apple': 3.0, 'Banana': 2.75, 'Orange': 3.5}
复制代码 In [303]:- #将每种水果的平均价格汇总到原始表格中
- dic = {
- 'Apple':3.00,
- 'Banana':2.75,
- 'Orange':3.50
- }
- #dic = mean_price.to_dict()
- df['mean_price'] = df['item'].map(dic)
- df
复制代码 Out[303]: itempricecolorweightmean_price0Apple4.0red123.001Banana3.0yellow202.752Orange3.0yellow503.503Banana2.5green302.754Orange4.0green203.505Apple2.0green443.00 In [311]:- #计算不同颜色水果的最大重量
- color_max_weight = df.groupby(by='color')['weight'].max()
- color_max_weight
复制代码 Out[311]:- color
- green 44
- red 12
- yellow 50
- Name: weight, dtype: int64
复制代码 In [312]:- df['max_weight'] = df['color'].map(color_max_weight.to_dict())
- df
复制代码 Out[312]: itempricecolorweightmean_pricemax_weight0Apple4.0red123.00121Banana3.0yellow202.75502Orange3.0yellow503.50503Banana2.5green302.75444Orange4.0green203.50445Apple2.0green443.0044
In [315]:- #求每种水果的平均价格和最高价格、最低价格
- df.groupby(by='item')['price'].agg(['mean','max','min'])
复制代码 Out[315]: meanmaxminitem Apple3.004.02.0Banana2.753.02.5Orange3.504.03.0 透视表¶
透视表是一种可以对数据动态排布并且分类汇总的表格格式。或许大多数人都在Excel使用过数据透视表,也体会到它的强大功能,而在pandas中它被称作pivot_table。
In [317]:- df = pd.read_csv('./data/透视表-篮球赛.csv')
- df.head(3)
复制代码 Out[317]: 对手胜负主客场命中投篮数投篮命中率3分命中率篮板助攻得分0勇士胜客10230.4350.444611271国王胜客8210.3810.28639272小牛胜主10190.5260.4623729 In [319]:- #根据胜负字段进行数据的分组,然后对每组数据进行均值计算
- df.pivot_table(index='对手',aggfunc='mean')
复制代码 Out[319]: 3分命中率助攻命中得分投篮命中率投篮数篮板对手 76人0.3395010.009.028.000.440520.53.5勇士0.4440011.0010.027.000.435023.06.0国王0.286009.008.027.000.381021.03.0太阳0.545007.0012.048.000.545022.02.0小牛0.462007.0010.029.000.526019.03.0尼克斯0.369009.5010.534.000.417525.03.5开拓者0.571003.0016.048.000.552029.08.0掘金0.143009.006.021.000.375016.08.0步行者0.2915012.508.527.500.396521.56.5湖人0.444009.0013.036.000.591022.04.0灰熊0.350257.758.527.250.401521.04.5爵士0.604008.0013.542.500.590522.03.5猛龙0.2730011.008.038.000.320025.06.0篮网0.615008.0013.037.000.650020.010.0老鹰0.5450011.008.029.000.533015.03.0骑士0.4290013.008.035.000.381021.011.0鹈鹕0.4000017.008.026.000.500016.01.0黄蜂0.4000011.008.027.000.444018.010.0 In [323]:- #根据胜负字段进行数据的分组,对分组中的篮板和得分两个字段进行求和运算
- df.pivot_table(index='胜负',values=['篮板','得分'],aggfunc='sum')
复制代码 Out[323]: 得分篮板胜负 胜692108负10919 In [322]:- #根据主客场字段进行数据分类后,对分类后的得分字段求最大值、篮板字段求均值和助攻字段求累加和操作
- df.pivot_table(index='主客场',aggfunc={'得分':'max','篮板':'mean','助攻':'sum'})
复制代码 Out[322]: 助攻得分篮板主客场 主121565.333333客116484.846154 In [324]:- #获取所有队主客场的总得分
- df.pivot_table(index='主客场',values='得分',aggfunc='sum')
复制代码 Out[324]: 得分主客场 主397客404 In [326]:- #查看主客场下的总得分都是哪些具体球队的得分构成的
- df.pivot_table(index='主客场',values='得分',aggfunc='sum',columns='对手')
复制代码 Out[326]: 对手76人勇士国王太阳小牛尼克斯开拓者掘金步行者湖人灰熊爵士猛龙篮网老鹰骑士鹈鹕黄蜂主客场 主29.0NaNNaNNaN29.037.0NaN21.029.0NaN60.056.038.037.0NaN35.026.0NaN客27.027.027.048.0NaN31.048.0NaN26.036.049.029.0NaNNaN29.0NaNNaN27.0 In [327]:- #查看主客场下的总得分都是哪些具体球队的得分构成的
- df.pivot_table(index='主客场',values='得分',aggfunc='sum',columns='对手',fill_value=0)
复制代码 Out[327]: 对手76人勇士国王太阳小牛尼克斯开拓者掘金步行者湖人灰熊爵士猛龙篮网老鹰骑士鹈鹕黄蜂主客场 主29000293702129060563837035260客272727480314802636492900290027 In [330]:- #多条件分类汇总操作
- df.pivot_table(index=['主客场','对手'],values='得分',aggfunc='sum')
复制代码 Out[330]: 得分主客场对手 主76人29小牛29尼克斯37掘金21步行者29灰熊60爵士56猛龙38篮网37骑士35鹈鹕26客76人27勇士27国王27太阳48尼克斯31开拓者48步行者26湖人36灰熊49爵士29老鹰29黄蜂27
- 快捷键:
- 增加cell:a,b
- 删除cell:x
- 运行cell:shift+enter
今日重点:数据清洗、map映射和map充当运算工具、groupby分组聚合、pivot_table透视¶
In [ ]: 手机销量分析案例¶
In [46]:- #加载数据
- import pandas as pd
- data = pd.read_excel('./data/Phone.xlsx')
复制代码 In [47]: In [48]:- #查看不同品牌手机的累计销量和累计销售额,且对累计销量进行降序
复制代码 In [49]: In [50]: In [51]: In [52]: 美国大选政治现金分析:
<ul>加载数据
查看数据的基本信息
指定数据截取,将如下字段的数据进行提取,其他数据舍弃
- cand_nm :候选人姓名
- contbr_nm : 捐赠人姓名
- contbr_st :捐赠人所在州
- contbr_employer : 捐赠人所在公司
- contbr_occupation : 捐赠人职业
- contb_receipt_amt :捐赠数额(美元)
- contb_receipt_dt : 捐款的日期
对新数据进行总览,查看是否存在缺失数据
用统计学指标快速描述数值型属性的概要。
空值处理。可能因为忘记填写或者保密等等原因,相关字段出现了空值,将其填充为NOT PROVIDE
异常值处理。将捐款金额 |