前言 开刷Pandas数据分析,看起来很好理解,不过没做笔记没敲代码心里总是不安稳,所以复现下课程代码并演示其中遇到的问题,顺便水一水笔记好了
参考资料:
课程视频链接:Pandas数据分析从入门到实战
数据及代码示例:ant-learn-pandas: pandas学习课程代码仓库 (gitee.com)
一、数据读取 0. 数据类型
数据类型
说明
Pandas读取方法
csv, tsv, txt
用逗号、tab或其它字符分割的文本文件
read_csv
excel
xls或xlsx文件
read_excel
mysql
关系型数据表
read_sql
1. read_csv 1.1 读取csv文件 csv是以逗号分割的文本文件,如下:
1 2 3 4 5 userId,movieId,rating,timestamp 1,1,4.0,964982703 1,3,4.0,964981247 1,6,4.0,964982224 1,47,5.0,964983815
直接使用read_csv读取该文件
1 2 3 fpath = "../datas/ml-latest-small/ratings.csv" ratings = pd.read_csv(fpath)
1.2 指定分割符 已知access_pvuv.txt如下:1 2 3 4 5 6 7 8 9 10 2019-09-10 139 92 2019-09-09 185 153 2019-09-08 123 59 2019-09-07 65 40 2019-09-06 157 98 2019-09-05 205 151 2019-09-04 196 167 2019-09-03 216 176 2019-09-02 227 148 2019-09-01 105 61
为read_csv添加参数delimiter (或seq) 指定分隔符,header=None 表示没有第一行列名称1 2 3 4 5 6 7 8 fpath = "../datas/crazyant/access_pvuv.txt" pvuv = pd.read_csv( fpath, delimiter='\t' , header=None , names=['date' , 'pv' , 'uv' ] )
分割符也可以是多个字符,但是seq指定多个字符默认会被当作正则表达式,需指定engine=’python’
1 2 3 4 5 6 7 df_ratings = pd.read_csv( "../datas/movielens-1m/ratings.dat" , sep="::" , engine='python' , names="UserID::MovieID::Rating::Timestamp" .split("::" ) )
2. read_excel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 fpath = "../datas/crazyant/access_pvuv.xlsx" pvuv = pd.read_excel(fpath) print (pvuv)''' 输出结果 日期 PV UV 0 2019-09-10 139 92 1 2019-09-09 185 153 2 2019-09-08 123 59 3 2019-09-07 65 40 4 2019-09-06 157 98 5 2019-09-05 205 151 6 2019-09-04 196 167 7 2019-09-03 216 176 8 2019-09-02 227 148 9 2019-09-01 105 61 '''
3. read_sql 3.1 使用pymysql 连接数据库,选择编码方式
1 2 3 4 5 6 7 8 9 10 import pandas as pdimport pymysqlconn = pymysql.connect( host='localhost' , user='root' , password='password' , database='dbname' , charset='utf8' )
1 2 3 4 5 6 7 8 9 table = pd.read_sql("select * from tbname" , con=conn) print (table)''' 输出结果 id preorder_traversal_string 0 1 4_2_1_0_#_#_#_3_#_#_8_7_#_#_11_#_#_ 1 2 5_4_2_#_#_1_7_#_6_#_#_#_3_0_#_5_1_#_#_#_6_#_#_ 2 3 6_#_7_5_3_#_#_1_#_#_2_8_#_#_#_ '''
但是会报警告:
UserWarning: pandas only supports SQLAlchemy connectable (engine/connection) or database string URI or sqlite3 DBAPI2 connection. Other DBAPI2 objects are not tested. Please consider using SQLAlchemy.
这里建议我们使用SQLAlchemy
3.2 使用SQLAlchemy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import pandas as pdfrom sqlalchemy import create_enginehost = "127.0.0.1" user = "root" password = "password" database = "dbname" engine = create_engine(f"mysql+pymysql://{user} :{password} @{host} /{database} " ) sql = 'select * from tbname' table = pd.read_sql(sql=sql, con=engine) print (table)''' 输出结果 id preorder_traversal_string 0 1 4_2_1_0_#_#_#_3_#_#_8_7_#_#_11_#_#_ 1 2 5_4_2_#_#_1_7_#_6_#_#_#_3_0_#_5_1_#_#_#_6_#_#_ 2 3 6_#_7_5_3_#_#_1_#_#_2_8_#_#_#_ '''
4. 查看数据格式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 print (ratings.head())''' 输出结果 userId movieId rating timestamp 0 1 1 4.0 964982703 1 1 3 4.0 964981247 2 1 6 4.0 964982224 3 1 47 5.0 964983815 4 1 50 5.0 964982931 ''' print (ratings.shape)''' 输出结果 (100836, 4) ''' print (ratings.columns)''' 输出结果 Index(['userId', 'movieId', 'rating', 'timestamp'], dtype='object') ''' print (ratings.index)''' 输出结果 RangeIndex(start=0, stop=100836, step=1) ''' print (ratings.dtypes)''' 输出结果 userId int64 movieId int64 rating float64 timestamp int64 dtype: object '''
二、数据结构 1. Series 1 2 3 4 5 6 7 8 9 10 11 12 import pandas as pds1 = pd.Series(list ('abcd' )) print (s1)''' 0 a 1 b 2 c 3 d dtype: object '''
1 2 3 4 5 6 7 8 9 10 11 12 s2 = pd.Series(list ('efgh' ), index=list ('abcd' )) print (s2)print (s2.index)''' a e b f c g d h dtype: object Index(['a', 'b', 'c', 'd'], dtype='object') '''
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 dict = { 'a' : 'e' , 'b' : 'f' , 'c' : 'g' , 'd' : 'h' } s3 = pd.Series(dict ) print (s3)''' a e b f c g d h dtype: object '''
2. DataFrame 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import pandas as pddata = { 'state' : ['Ohio' , 'Ohio' , 'Ohio' , 'Nevada' , 'Nevada' ], 'year' : [2000 , 2001 , 2002 , 2001 , 2002 ], 'pop' : [1.5 , 1.7 , 3.6 , 2.4 , 2.9 ] } df = pd.DataFrame(data) print (df)''' state year pop 0 Ohio 2000 1.5 1 Ohio 2001 1.7 2 Ohio 2002 3.6 3 Nevada 2001 2.4 4 Nevada 2002 2.9 '''
1 2 3 4 5 6 7 8 9 10 11 12 print (df.index, '\n\n' , df.columns, '\n\n' , df.dtypes)''' RangeIndex(start=0, stop=5, step=1) Index(['state', 'year', 'pop'], dtype='object') state object year int64 pop float64 dtype: object '''
三、查询数据 0. 查询方法
df.loc :基于标签索引,结果包含最后一个标签的值
df.iloc:基于位置索引,结果不包含最后一个位置的值
df.where
df.query
本节主要介绍df.loc
1. 数据预处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import pandas as pddf = pd.read_csv("../datas/beijing_tianqi/beijing_tianqi_2018.csv" ) print (df.head())''' ymd bWendu yWendu tianqi fengxiang fengli aqi aqiInfo aqiLevel 0 2018-01-01 3℃ -6℃ 晴~多云 东北风 1-2级 59 良 2 1 2018-01-02 2℃ -5℃ 阴~多云 东北风 1-2级 49 优 1 2 2018-01-03 2℃ -5℃ 多云 北风 1-2级 28 优 1 3 2018-01-04 0℃ -8℃ 阴 东北风 1-2级 28 优 1 4 2018-01-05 3℃ -6℃ 多云~晴 西北风 1-2级 50 优 '''
1 2 df.set_index('ymd' , inplace=True )
1 2 3 4 5 df.loc[:, "bWendu" ] = df["bWendu" ].str .replace("℃" , "" ).astype('int32' ) df.loc[:, "yWendu" ] = df["yWendu" ].str .replace("℃" , "" ).astype('int32' )
1 2 3 4 5 6 7 8 9 10 print (df.head())''' bWendu yWendu tianqi fengxiang fengli aqi aqiInfo aqiLevel ymd 2018-01-01 3 -6 晴~多云 东北风 1-2级 59 良 2 2018-01-02 2 -5 阴~多云 东北风 1-2级 49 优 1 2018-01-03 2 -5 多云 北风 1-2级 28 优 1 2018-01-04 0 -8 阴 东北风 1-2级 28 优 1 2018-01-05 3 -6 多云~晴 西北风 1-2级 50 优 '''
这里会报一个警告:1 2 3 DeprecationWarning: In a future version , `df.iloc [:, i ] = newvals ` will attempt to set the values inplace instead of always setting a new array . To retain the old behavior , use either `df [df.columns [i ]] = newvals ` or , if columns are non -unique , `df.isetitem (i , newvals )`
2. 按数值、列表、区间查询 1 2 single_value = df.loc['2018-01-03' , 'bWendu' ]
1 2 3 s1 = df.loc['2018-01-03' , ['bWendu' , 'yWendu' ]] s2 = df.loc[['2018-01-03' , '2018-01-04' , '2018-01-05' ], 'bWendu' ]
1 2 df2 = df.loc[['2018-01-03' ,'2018-01-04' ,'2018-01-05' ], ['bWendu' , 'yWendu' ]]
1 2 df3 = df.loc['2018-01-03' :'2018-01-05' , 'bWendu' :'fengxiang' ]
3. 条件查询 1 2 3 4 5 6 7 8 9 10 df4 = df.loc[(df["bWendu" ] <= 30 ) & (df["yWendu" ] >= 15 ) & (df["tianqi" ] == '晴' ) & (df["aqiLevel" ] == 1 ), :] print (df4)''' bWendu yWendu tianqi fengxiang fengli aqi aqiInfo aqiLevel ymd 2018-08-24 30 20 晴 北风 1-2级 40 优 1 2018-09-07 27 16 晴 西北风 3-4级 22 优 1 '''
其中,条件表达式返回的是一个布尔值的Series
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 print (df["yWendu" ] < -10 )''' 2018-01-01 False 2018-01-02 False 2018-01-03 False 2018-01-04 False 2018-01-05 False ... 2018-12-27 True 2018-12-28 True 2018-12-29 True 2018-12-30 True 2018-12-31 False Name: yWendu, Length: 365, dtype: bool '''
4. 函数查询 1 2 df5 = df.loc[lambda df : (df["bWendu" ] <= 30 ) & (df["yWendu" ] >= 15 ), :]
1 2 3 4 5 def query_my_data (df ): return df.index.str .startswith("2018-09" ) & (df["aqiLevel" ] == 1 ) df6 = df.loc[query_my_data, :]
四、新增数据列 1. 直接赋值 1 df.loc[:, 'wencha' ] = df['bWendu' ] - df['yWendu' ]
2. df.apply 传入一个函数并选定axis:
当axis=1,函数的参数为一行的Series(常用)
当axis=0,函数的参数为一列的Series
1 2 3 4 5 6 7 8 def get_wendu_type (x ): if x["bWendu" ] > 33 : return '高温' if x["yWendu" ] < -10 : return '低温' return '常温' df.loc[:, "wendu_type" ] = df.apply(get_wendu_type, axis=1 )
3. df.assign df.assign总是会创建一个新的copy
利用lambda表达式,处理原来的数据得到新列
1 2 3 4 5 6 # 可以同时添加多个新的列 df.assign( yWendu_huashi = lambda x : x["yWendu"] * 9 / 5 + 32, # 摄氏度转华氏度 bWendu_huashi = lambda x : x["bWendu"] * 9 / 5 + 32 )
4. Series.map 传入一个字典,将Series的值根据字典映射
示例如下:
原始数据
1 2 3 stocks = pd.read_excel('../datas/stocks/互联网公司股票.xlsx' ) print (stocks.head(), '\n' , stocks['公司' ].unique())
1 2 3 4 5 6 7 日期 公司 收盘 开盘 高 低 交易量 涨跌幅 0 2019 -10 -03 BIDU 104 .32 102 .35 104 .73 101 .15 2 .24 0 .02 1 2019 -10 -02 BIDU 102 .62 100 .85 103 .24 99 .50 2 .69 0 .01 2 2019 -10 -01 BIDU 102 .00 102 .80 103 .26 101 .00 1 .78 -0 .01 3 2019 -10 -03 BABA 169 .48 166 .65 170 .18 165 .00 10 .39 0 .02 4 2019 -10 -02 BABA 165 .77 162 .82 166 .88 161 .90 11 .60 0 .00 ['BIDU' 'BABA' 'IQ' 'JD']
现将 公司 这一列通过字典对应到该公司的中文名
1 2 3 4 5 6 7 8 9 10 dict_company_names = { "bidu" : "百度" , "baba" : "阿里巴巴" , "iq" : "爱奇艺" , "jd" : "京东" } stocks['公司中文' ] = stocks['公司' ].str .lower().map (dict_company_names) print (stocks['公司中文' ].unique())
1 ['百度' '阿里巴巴' '爱奇艺' '京东']
其实map也可以像apply一样传一个函数,修改Series的每个值
5. 条件选择分组后赋值
错误示例:
1 2 3 df.loc[df["bWendu" ]-df["yWendu" ]>10 ]["wencha_type" ] = "温差大" df.loc[df["bWendu" ]-df["yWendu" ]<=10 ]["wencha_type" ] = "温差正常"
两个[]的链式操作相当于1 df.get(condition).set (wen_cha)
这里get得到的结果可能是view也可能是copy,存在歧义
正确示范:
1 2 3 df.loc[df["bWendu" ]-df["yWendu" ]>10 , "wencha_type" ] = "温差大" df.loc[df["bWendu" ]-df["yWendu" ]<=10 , "wencha_type" ] = "温差正常"
五、聚合查询 1. describe输出统计结果
1 2 3 4 5 6 7 8 9 bWendu yWendu aqi aqiLevel wencha count 365 .000000 365 .000000 365 .000000 365 .000000 365 .000000 mean 18 .665753 8 .358904 82 .183562 2 .090411 10 .306849 std 11 .858046 11 .755053 51 .936159 1 .029798 2 .781233 min -5 .000000 -12 .000000 21 .000000 1 .000000 2 .000000 25 % 8 .000000 -3 .000000 46 .000000 1 .000000 8 .000000 50 % 21 .000000 8 .000000 69 .000000 2 .000000 10 .000000 75 % 29 .000000 19 .000000 104 .000000 3 .000000 12 .000000 max 38 .000000 27 .000000 387 .000000 6 .000000 18 .000000
describe只能得到数值列的统计结果
2. 非数值列统计 2.1 unique唯一去重 1 print (df['tianqi' ].unique())
1 2 3 4 ['晴~多云' '阴~多云' '多云' '阴' '多云~晴' '多云~阴' '晴' '阴~小雪' '小雪~多云' '小雨~ 阴' '小雨~雨夹雪' '多云~小雨' '小雨~多云' '大雨~小雨' '小雨' '阴~小雨' '多云~雷阵雨' '雷阵雨~多云' '阴~ 雷阵雨' '雷阵雨' '雷阵雨~大雨' '中雨~雷阵雨' '小雨~大雨' '暴雨~雷阵雨' '雷阵雨~中雨' '小雨~雷阵雨' '雷 阵雨~阴' '中雨~小雨' '小雨~中雨' '雾~多云' '霾']
2.2 value_counts按值计数 1 print (df['wencha_type' ].value_counts())
1 2 3 温差正常 187 温差大 178 Name: wencha_type , dtype : int64
3. 协方差和相关系数 1 print (df.cov(), '\n\n' , df.corr())
1 2 3 4 5 6 7 8 9 10 11 12 13 bWendu yWendu aqi aqiLevel wencha bWendu 140 .613247 135 .529633 47 .462622 0 .879204 5 .083614 yWendu 135 .529633 138 .181274 16 .186685 0 .264165 -2 .651641 aqi 47 .462622 16 .186685 2697 .364564 50 .749842 31 .275937 aqiLevel 0 .879204 0 .264165 50 .749842 1 .060485 0 .615038 wencha 5 .083614 -2 .651641 31 .275937 0 .615038 7 .735255 bWendu yWendu aqi aqiLevel wencha bWendu 1 .000000 0 .972292 0 .077067 0 .071999 0 .154142 yWendu 0 .972292 1 .000000 0 .026513 0 .021822 -0 .081106 aqi 0 .077067 0 .026513 1 .000000 0 .948883 0 .216523 aqiLevel 0 .071999 0 .021822 0 .948883 1 .000000 0 .214740 wencha 0 .154142 -0 .081106 0 .216523 0 .214740 1 .000000
4. 分组后聚合查询 4.1 pd.groupby DataFrame.groupby(by, axis=0, as_index=True, sort=True, group_keys=True, dropna=True)
by: 字符串或者List<字符串>,用于指定分组
axis:默认为0,对行做分组
as_index:将分组的keys作为索引值,默认为True
sort:将结果按分组的keys排序,默认为True
group_keys:与as_index相同,但是只有在使用apply时生效,在使用聚合时失效(都为True)
DataFrame.groupby的返回值是一个DataFrameGroupBy对象
4.2 聚合查询示例 1) 使用已有的聚合函数
1 2 3 4 df = pd.DataFrame({'A' : ['foo' , 'bar' , 'foo' , 'bar' , 'foo' , 'bar' , 'foo' , 'foo' ], 'B' : ['one' , 'one' , 'two' , 'three' , 'two' , 'two' , 'one' , 'three' ], 'C' : np.random.randn(8 ), 'D' : np.random.randn(8 )})
1 2 3 4 5 6 A B C D 0 foo one 0 .411257 -1 .345015 1 bar one 0 .680037 0 .225904 2 foo two -0 .640561 -0 .447943 3 bar three -0 .354500 -0 .517335 4 foo two 1 .092376 -0 .978323
agg列表传参,对不同列使用相同聚合函数:1 df_group1 = df.groupby(['A' , 'B' ]).agg([np.sum , np.mean])
1 2 3 4 5 6 7 8 9 C D sum mean sum mean A B bar one 0 .680037 0 .680037 0 .225904 0 .225904 three -0 .354500 -0 .354500 -0 .517335 -0 .517335 two -1 .047294 -1 .047294 -1 .879435 -1 .879435 foo one 0 .419079 0 .209540 -1 .893515 -0 .946758 three -0 .681836 -0 .681836 1 .008956 1 .008956 two 0 .451816 0 .225908 -1 .426266 -0 .713133
agg对象传参,对不同列使用不同聚合函数:1 df_group2 = df.groupby(['A' , 'B' ]).agg({'C' : np.sum , 'D' : np.mean})
1 2 3 4 5 6 7 8 C D A B bar one 0 .680037 0 .225904 three -0 .354500 -0 .517335 two -1 .047294 -1 .879435 foo one 0 .419079 -0 .946758 three -0 .681836 1 .008956 two 0 .451816 -0 .713133
DataFrameGroupBy对象原生的函数:1 df_group3 = df.groupby('A' )['C' ].mean()
1 2 3 4 A bar -0 .240586 foo 0 .037812 Name: C , dtype : float64
2)自定义聚合函数
DataFrameGroupBy.apply(func, args, \ *kwargs)
func:自定义的聚合函数,第一个参数是一个DataFrame
args, \ *kwargs:func第二个及往后的参数(如有)
使用的数据为多次出现在例子中的天气数据,增加一列为月份,由ymd年月日得到:1 2 df['month' ] = df['ymd' ].str [:7 ]
1 2 3 4 5 6 ymd bWendu yWendu tianqi ... aqi aqiInfo aqiLevel month 0 2018 -01 -01 3 -6 晴~多云 ... 59 良 2 2018 -01 1 2018 -01 -02 2 -5 阴~多云 ... 49 优 1 2018 -01 2 2018 -01 -03 2 -5 多云 ... 28 优 1 2018 -01 3 2018 -01 -04 0 -8 阴 ... 28 优 1 2018 -01 4 2018 -01 -05 3 -6 多云~晴 ... 50 优 1 2018 -01
对月份做分组,并对分组后的数据对最高温度做排序,返回最高温度最大的3行
1 2 3 4 def getTopN (df, n ): return df.sort_values(by='bWendu' )[['ymd' , 'bWendu' , 'yWendu' ]][-n:] df_group = df.groupby('month' ).apply(getTopN, 3 )
1 2 3 4 5 6 7 ymd bWendu yWendu month 2018 -01 16 2018 -01 -17 6 -7 13 2018 -01 -14 6 -5 18 2018 -01 -19 7 -4 2018 -02 58 2018 -02 -28 9 -2 53 2018 -02 -23 10 -4
六、缺失值处理 1. 处理方式 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
2. 数据清洗示例
2.1 检测空值 1 2 3 4 5 studf = pd.read_excel("../datas/student_excel/student_excel.xlsx" , skiprows=2 ) print (studf.isnull())
1 2 3 4 5 6 7 8 9 10 11 12 Unnamed: 0 姓名 科目 分数 0 True False False False 1 True True False False 2 True True False False 3 True True True True 4 True False False False 5 True True False True 6 True True False False 7 True True True True 8 True False False False 9 True True False False 10 True True False False
2.2 dropna示例 1 2 3 4 5 studf.dropna(axis=1 , how='all' , inplace=True ) studf.dropna(axis=0 , how='all' , inplace=True ) print (studf)
1 2 3 4 5 6 7 8 9 10 姓名 科目 分数 0 小明 语文 85 .0 1 NaN 数学 80 .0 2 NaN 英语 90 .0 4 小王 语文 85 .0 5 NaN 数学 NaN6 NaN 英语 90 .0 8 小刚 语文 85 .0 9 NaN 数学 80 .0 10 NaN 英语 90 .0
2.3 fillna示例 1 2 3 4 5 6 studf['分数' ].fillna(value=0 , inplace=True ) studf['姓名' ].fillna(method='ffill' , inplace=True ) print (studf)
1 2 3 4 5 6 7 8 9 10 姓名 科目 分数 0 小明 语文 85 .0 1 小明 数学 80 .0 2 小明 英语 90 .0 4 小王 语文 85 .0 5 小王 数学 0 .0 6 小王 英语 90 .0 8 小刚 语文 85 .0 9 小刚 数学 80 .0 10 小刚 英语 90 .0
七、数据排序 1. 排序方法 1) Series的排序: Series.sort_values(ascending=True, inplace=False) 参数说明:
ascending:默认为True升序排序,为False降序排序
inplace:是否修改原始Series
2) DataFrame的排序: DataFrame.sort_values(by, ascending=True, inplace=False) 参数说明:
by:字符串或者List<字符串>,单列排序或者多列排序
ascending:bool或者bool的列表,升序还是降序,如果是list对应by的多列
inplace:是否修改原始DataFrame
2. 排序示例 1 2 print (df['aqi' ].sort_values())
1 2 3 4 5 6 7 8 9 10 11 12 13 ymd 2018 -09 -29 21 2018 -10 -09 21 2018 -09 -07 22 2018 -09 -30 22 2018 -10 -29 22 ... 2018 -11 -14 266 2018 -03 -13 287 2018 -04 -02 287 2018 -03 -14 293 2018 -03 -28 387 Name: aqi , Length : 365, dtype : int64
1 2 3 df.sort_values(by=['aqi' , 'bWendu' ], ascending=[False , True ], inplace=True ) print (df[['aqi' , 'bWendu' ]])
1 2 3 4 5 6 7 8 9 10 11 12 13 aqi bWendu ymd 2018 -03 -28 387 25 2018 -03 -14 293 15 2018 -03 -13 287 17 2018 -04 -02 287 26 2018 -11 -14 266 13 ... ... ... 2018 -10 -29 22 15 2018 -09 -30 22 19 2018 -09 -07 22 27 2018 -10 -09 21 15 2018 -09 -29 21 22
八、字符串处理 0. Pandas的str Pandas的字符串处理:
使用方法:先获取Series的str属性,然后在属性上调用函数;
只能在字符串列上使用,不能数字列上使用;
Dataframe上没有str属性和处理方法
Series.str并不是Python原生字符串,而是自己的一套方法,不过大部分和原生str很相似;
1. 基础用法 获取str属性,并调用各种方法,如replace, isnumeric, len
1 print (df['wencha_type' ].str .len ())
1 2 3 4 5 6 7 8 9 10 11 12 13 ymd 2018 -03 -28 3 2018 -03 -14 4 2018 -03 -13 3 2018 -04 -02 3 2018 -11 -14 4 .. 2018 -10 -29 3 2018 -09 -30 4 2018 -09 -07 3 2018 -10 -09 3 2018 -09 -29 3 Name: wencha_type , Length : 365, dtype : int64
2. 条件查询 或使用contains, startswith等得到bool的Series做条件查询
1 print (df.loc[df['tianqi' ].str .startswith('多云' ), ['tianqi' , 'fengxiang' ]])
1 2 3 4 5 6 7 8 9 10 11 12 13 tianqi fengxiang ymd 2018 -03 -28 多云~晴 东风2018 -03 -14 多云~阴 东北风2018 -04 -02 多云 北风2018 -11 -14 多云 南风2018 -11 -26 多云 东南风... ... ... 2018 -01 -25 多云 东北风2018 -10 -10 多云~晴 西北风2018 -02 -03 多云 北风2018 -09 -30 多云 西北风2018 -10 -09 多云~晴 西北风
3. 正则表达式 由于Series.str天然支持正则表达式,示例如下:
匹配字符集合并做替换:
1 2 3 4 5 6 7 8 9 def get_nianyueri (x ): year,month,day = x["ymd" ].split("-" ) return f"{year} 年{month} 月{day} 日" df["中文日期" ] = df.apply(get_nianyueri, axis=1 ) df.loc[:, '中文日期' ] = df['中文日期' ].str .replace('[年月日]' , '' ) print (df['中文日期' ])
1 2 3 4 5 6 7 8 9 10 11 12 86 20180328 72 20180314 71 20180313 91 20180402 317 20181114 ... 301 20181029 272 20180930 249 20180907 281 20181009 271 20180929 Name: 中文日期, Length : 365, dtype : object
捕获组提取数据:1 2 extracted_fengli = df['fengli' ].str .extract(r'(\d)-(\d)' ) print (extracted_fengli.head())
1 2 3 4 5 6 7 8 9 10 11 12 0 1 86 1 2 72 1 2 71 1 2 91 1 2 317 1 2 .. .. .. 301 3 4 272 4 5 249 3 4 281 4 5 271 3 4
九、索引 0. 索引的作用 选择恰当的索引可以加速查询性能
当索引是唯一 的时,Pandas会用哈希表优化性能,时间复杂度为O(1)
当索引不唯一,但是单调 时,Pandas会使用二分查找,时间复杂度为O(log n)
当索引既不唯一且不单调时,Pandas只能遍历,时间复杂度为O(n)
因此,我们要判断当前索引是否为以上类型,尽可能选择唯一的索引,单调次之
1. 选择索引示例 原始数据如下:
1 2 3 4 5 6 userId movieId rating timestamp 0 1 1 4 .0 964982703 1 1 3 4 .0 964981247 2 1 6 4 .0 964982224 3 1 47 5 .0 964983815 4 1 50 5 .0 964982931
判断每一列是否存在唯一约束:
1 print (df.nunique() == len (df))
1 2 3 4 5 userId False movieId False rating False timestamp False dtype: bool
判断每一列是否单调:
1 2 3 4 5 6 7 is_monotonic_increasing = df.apply(lambda x: x.is_monotonic_increasing) is_monotonic_decreasing = df.apply(lambda x: x.is_monotonic_decreasing) print (is_monotonic_increasing, '\n\n' , is_monotonic_decreasing)
1 2 3 4 5 6 7 8 9 10 11 userId True movieId False rating False timestamp False dtype: bool userId False movieId False rating False timestamp False dtype : bool
2. 设置索引示例 DataFrame.set_index(keys, append=False, drop=True, inplace=False)
keys代表被用作索引的列
append代表是否保留原来的索引
drop表示是否将指定的列在原数据列中删除
inplace表示是否在原数据上修改
1 2 df.set_index('userId' ,append=True , drop=False , inplace=True ) print (df.head())
1 2 3 4 5 6 7 userId movieId rating timestamp userId 0 1 1 1 4 .0 964982703 1 1 1 3 4 .0 964981247 2 1 1 6 4 .0 964982224 3 1 1 47 5 .0 964983815 4 1 1 50 5 .0 964982931
十、数据合并 1. merge合并 1.1 函数定义 课件中给出的pd.merge 的解释如下:
pd.merge(left, right, how=‘inner’, on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=True, suffixes=(‘_x’, ‘_y’), copy=True, indicator=False, validate=None)
left,right:要merge的dataframe或者有name的Series
how:join类型,‘left’, ‘right’, ‘outer’, ‘inner’
on:join的key,left和right都需要有这个key
left_on:left的df或者series的key
right_on:right的df或者seires的key
left_index,right_index:使用index而不是普通的column做join
suffixes:两个元素的后缀,如果列有重名,自动添加后缀,默认是(‘_x’, ‘_y’)
如果学过SQL语句,可以很好地将两者对应起来:
pd.merge(left, right, on=’key’) 相当于 FROM left JOIN right ON left.key=right.key
pd.merge(left, right, left_on=’key1’, right_on=’key2’) 相当于 FROM left JOIN right ON left.key1=right.key2
而how=’left’/‘right’/‘outer’就相当于LEFT/RIGHT/FULL JOIN
1.2 合并示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 df_ratings = pd.read_csv( "../datas/movielens-1m/ratings.dat" , sep="::" , engine='python' , names="UserID::MovieID::Rating::Timestamp" .split("::" ) ) df_users = pd.read_csv( "../datas/movielens-1m/users.dat" , sep="::" , engine='python' , names="UserID::Gender::Age::Occupation::Zip-code" .split("::" ) ) print (df_ratings.head(), '\n' , df_users.head())df_joined1 = pd.merge(df_ratings, df_users, on='UserID' ) print (df_joined1.head())
输出结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 UserID MovieID Rating Timestamp 0 1 1193 5 978300760 1 1 661 3 978302109 2 1 914 3 978301968 3 1 3408 4 978300275 4 1 2355 5 978824291 UserID Gender Age Occupation Zip-code 0 1 F 1 10 48067 1 2 M 56 16 70072 2 3 M 25 15 55117 3 4 M 45 7 02460 4 5 M 25 20 55455 UserID MovieID Rating Timestamp Gender Age Occupation Zip-code 0 1 1193 5 978300760 F 1 10 48067 1 1 661 3 978302109 F 1 10 48067 2 1 914 3 978301968 F 1 10 48067 3 1 3408 4 978300275 F 1 10 48067 4 1 2355 5 978824291 F 1 10 48067
2. concat合并 2.1 函数定义 pandas.concat(objs, axis=0, join=‘outer’, ignore_index=False)
objs:一个列表,内容可以是DataFrame或者Series,可以混合
axis:默认是0代表按行合并,如果等于1代表按列合并
join:合并的时候索引的对齐方式,默认是outer join,也可以是inner join
ignore_index:是否忽略掉原来的数据索引
DataFrame.append(other, ignore_index=False)
append只有按行合并,没有按列合并,相当于concat按行的简写形式
other:单个dataframe、series、dict,或者列表
ignore_index:是否忽略掉原来的数据索引 -2.2 合并示例
原始数据:1 2 3 4 5 6 7 8 9 10 11 12 13 14 df1 = pd.DataFrame({'A' : ['A0' , 'A1' , 'A2' , 'A3' ], 'B' : ['B0' , 'B1' , 'B2' , 'B3' ], 'C' : ['C0' , 'C1' , 'C2' , 'C3' ], 'D' : ['D0' , 'D1' , 'D2' , 'D3' ], 'E' : ['E0' , 'E1' , 'E2' , 'E3' ] }) df2 = pd.DataFrame({ 'A' : ['A4' , 'A5' , 'A6' , 'A7' ], 'B' : ['B4' , 'B5' , 'B6' , 'B7' ], 'C' : ['C4' , 'C5' , 'C6' , 'C7' ], 'D' : ['D4' , 'D5' , 'D6' , 'D7' ], 'F' : ['F4' , 'F5' , 'F6' , 'F7' ] }) print (df1, '\n' , df2)
1 2 3 4 5 6 7 8 9 10 A B C D E 0 A0 B0 C0 D0 E01 A1 B1 C1 D1 E12 A2 B2 C2 D2 E23 A3 B3 C3 D3 E3 A B C D F 0 A4 B4 C4 D4 F41 A5 B5 C5 D5 F52 A6 B6 C6 D6 F63 A7 B7 C7 D7 F7
有无ignore_index的区别:
1 2 3 4 5 df3 = pd.concat([df1, df2]) df4 = pd.concat([df1, df2], ignore_index=True ) print (df3, '\n' , df4)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 A B C D E F 0 A0 B0 C0 D0 E0 NaN1 A1 B1 C1 D1 E1 NaN2 A2 B2 C2 D2 E2 NaN3 A3 B3 C3 D3 E3 NaN0 A4 B4 C4 D4 NaN F41 A5 B5 C5 D5 NaN F52 A6 B6 C6 D6 NaN F63 A7 B7 C7 D7 NaN F7 A B C D E F 0 A0 B0 C0 D0 E0 NaN1 A1 B1 C1 D1 E1 NaN2 A2 B2 C2 D2 E2 NaN3 A3 B3 C3 D3 E3 NaN4 A4 B4 C4 D4 NaN F45 A5 B5 C5 D5 NaN F56 A6 B6 C6 D6 NaN F67 A7 B7 C7 D7 NaN F7
默认为outer,使用inner如下:1 2 3 df5 = pd.concat([df1, df2], join='inner' ) print (df5)
1 2 3 4 5 6 7 8 9 A B C D 0 A0 B0 C0 D01 A1 B1 C1 D12 A2 B2 C2 D23 A3 B3 C3 D30 A4 B4 C4 D41 A5 B5 C5 D52 A6 B6 C6 D63 A7 B7 C7 D7
十一、数据透视 1. 透视方法 1) stack 和 unstack
stack和unstack互为逆操作
stack将列索引 转成最内层行索引
而unstack将最内层行索引 转成列索引
具体参考:pandas中DataFrame的stack()、unstack()和pivot()方法的对比_pandas pivot和stack区别-CSDN博客
2) reset_index 和 pivot
DataFrame.pivot(index, columns, values)
指定三列,依次为行索引、列索引、构建一个DataFrame
使用rest_index可实现逆操作
2. 透视示例 2.1 数据预处理 原始数据:
1 2 3 4 5 6 7 8 9 df = pd.read_csv( "../datas/movielens-1m/ratings.dat" , header=None , names="UserID::MovieID::Rating::Timestamp" .split("::" ), sep="::" , engine="python" ) print (df.head())
1 2 3 4 5 6 UserID MovieID Rating Timestamp 0 1 1193 5 978300760 1 1 661 3 978302109 2 1 914 3 978301968 3 1 3408 4 978300275 4 1 2355 5 978824291
调用pd.to_datetime将时间戳转化为时间对象,通过访问对象的属性dt.month访问月份,并做统计操作1 2 3 4 5 6 df["pdate" ] = pd.to_datetime(df["Timestamp" ], unit='s' ) df_group = df.groupby([df['pdate' ].dt.month, 'Rating' ])['UserID' ].count() print (df_group.head())
1 2 3 4 5 6 7 pdate Rating 1 1 1127 2 2608 3 6442 4 8400 5 4495 Name: UserID , dtype : int64
2.2 使用透视 1) unstack 和 stack示例
1 2 3 df_unstack = df_group.unstack() print(df_unstack.head())
1 2 3 4 5 6 7 Rating 1 2 3 4 5 pdate 1 1127 2608 6442 8400 4495 2 629 1464 3297 4403 2335 3 466 1077 2523 3032 1439 4 1048 2247 5501 6748 3863 5 4557 7631 18481 25769 17840
绘制曲线1 2 df_unstack.plot() plt.show()
2) reset_index 和 pivot示例
1 2 3 df_reset = df_group.reset_index() print (df_reset.head())
1 2 3 4 5 6 pdate Rating pv 0 1 1 1127 1 1 2 2608 2 1 3 6442 3 1 4 8400 4 1 5 4495
1 2 3 df_pivot = df_reset.pivot('pdate' , 'Rating' , 'pv' ) print (df_pivot.head())
1 2 3 4 5 6 7 Rating 1 2 3 4 5 pdate 1 1127 2608 6442 8400 4495 2 629 1464 3297 4403 2335 3 466 1077 2523 3032 1439 4 1048 2247 5501 6748 3863 5 4557 7631 18481 25769 17840