一、数据加载、存储和文件格式
函数 | 说明 |
---|---|
read_csv | 从文件、URL、文件型对象中加载带分隔符的数据。默认分隔符为逗号 |
read_table | 从文件、URL、文件型对象中加载带分隔符的数据。默认分隔符为制表符(“\t”) |
read_fwf | 读取定宽列格式数据(也就是或,没有分隔符) |
read_clipboard | 读取剪贴板中的数据,可以看做 read_table 的剪贴板版。再将网页转换为表格时很有用 |
read_excel | 从 Excel XLS 或 XLSX file 读取表格数据 |
read_hdf | 读取 pandas 写的 HDF5 文件 |
read_html | 读取 HTML 文档中的所有表格 |
read_json | 读取JSON(JavaScript Object Notation)字符串中的数据 |
read_msgpack | 二进制格式编码的 pandas 数据 |
read_pickle | 读取 Python pickle 格式中存储的任意对象 |
read_sas | 读取存储于 SAS 系统自定义存储格式的 SAS 数据集 |
read_sql | (使用 SQLAlchemy)读取 SQL 查询结果为 pandas 的 DataFrame |
read_stata | 读取 Stata 文件格式的数据集 |
参数 | 说明 |
---|---|
path | 表示文件系统位置、URL、文件型对象的字符串 |
sep或delimiter | 用于对行中各字段进行拆分的字符序列或正则表达式 |
header | 用作列名和行号。默认为0(第一行),如果没有header行就应该设置为None |
index_col | 用作行索引的列编号或列名。可以是单个名称/数字或由多个名称/数字组成的列表(层次化索引) |
names | 用于结果列名列表,结合header=None |
skiprows | 需要忽略的行数(文件开始处算起),或需要跳过的行号列表(从0开始) |
na_values | 一组用于替换NA的值 |
comment | 用于将注释信息从行尾拆分出来的字符(一个或多个) |
parse_dates | 尝试将数据解析为日期,默认为False,如果为True,则尝试解析所有列。此外,还可以指定需要解析的一组列好或列名。如果列表的元素为列表或元组,就会将多个列组合到一起再进行日期解析工作(例如,日期/时间分别位于两个列中) |
keep_deta_col | 如果连接多列解析日期,则保持参与连接的列。默认为False |
converters | 由列号/列名跟函数之间的映射关系组成的字典。例如,[‘foo’:f]会对foo列的所有值应用函数f |
dayfirst | 当解析有歧义的日期时,将其看做国际格式(例如,7/6/2012 -> June 7,2012)。默认为False |
date_parser | 用于解析日期的函数 |
nrows | 需要读取的行数(从文件开始处算起) |
iterator | 返回一个TextParser以便逐块读取文件 |
chunksize | 文件块的大小(用于迭代) |
skip_footer | 需要忽略的函数(从文件末尾处算起) |
1.1 读写文本格式文件
1.1.1 读文本的方式
import pandas as pd
import numpy as np
df = pd.read_csv('pandas_test.csv')
print(df,'\n')
1 2 3 4 message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 python
df = pd.read_table('pandas_test.csv',sep=',')
print(df,'\n')
1 2 3 4 message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 python
# 读取时使用默认列索引
df = pd.read_csv('pandas_test.csv',header=None)
print(df,'\n')
0 1 2 3 4
0 1 2 3 4 message
1 1 2 3 4 hello
2 5 6 7 8 world
3 9 10 11 12 python
# 读取时设置列索引
# names设置列索引后,会把csv所有数据作为的数据内容
df = pd.read_csv('pandas_test.csv',names=list('abcd')+['message'])
print(df,'\n')
a b c d message
0 1 2 3 4 message
1 1 2 3 4 hello
2 5 6 7 8 world
3 9 10 11 12 python
# index_col参数,设置某一列作为dataframe的行索引
df = pd.read_csv('pandas_test.csv',names=list('abcd')+['message'],index_col='message')
print(df,'\n')
a b c d
message
message 1 2 3 4
hello 1 2 3 4
world 5 6 7 8
python 9 10 11 12
# 设置层次索引
df1 = pd.read_csv('csv_mindex.csv')
print(df1,'\n')
df1 = pd.read_csv('csv_mindex.csv',index_col=['key1','key2'])
print(df1,'\n')
key1 key2 value1 value2
0 one a 1 2
1 one b 3 4
2 one c 5 6
3 one d 6 8
4 two a 9 10
5 two b 11 12
6 two c 13 14
7 two d 15 16
value1 value2
key1 key2
one a 1 2
b 3 4
c 5 6
d 6 8
two a 9 10
b 11 12
c 13 14
d 15 16
# 分块读取大文件
# 设置 chunksize 的大小,会每次读取部分的数据
agg1 = pd.read_csv('csv_mindex.csv',chunksize=1)
print(type(agg1))
<class 'pandas.io.parsers.readers.TextFileReader'>
# 每次执行当前代码会分块读取相关数值
print(agg1.get_chunk())
key1 key2 value1 value2
0 one a 1 2
# iterator 设置读取到迭代器对象(只能遍历一次)
agg2 = pd.read_csv('csv_mindex.csv',iterator=True)
print(type(agg2))
<class 'pandas.io.parsers.readers.TextFileReader'>
# 使用 get_chunk 获取指定多个数据
print(agg2.get_chunk(3),'\n')
for i in agg2:
print(i)
key1 key2 value1 value2
0 one a 1 2
1 one b 3 4
2 one c 5 6
key1 key2 value1 value2
3 one d 6 8
4 two a 9 10
5 two b 11 12
6 two c 13 14
7 two d 15 16
1.1.2 写文本的方式
df2 = pd.read_csv('pandas_test.csv')
print(df2)
1 2 3 4 message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 python
# 写的方法
df2.to_csv('out_ex1.csv')
1.2 JSON数据
obj = """
{"name":"Wes",
"pet":null,
"places_lived":["United States","Spain","Germany"],
"siblings":[{"age":30,"name":"Scott","pets":["Zeus","Zuko"]},
{"age":38,"name":"Katie","pets":["Sixes","Stache","Cisco"]}]
}
"""
# 导入python的json库
import json
# 将json字符串转换为python类型数据
res = json.loads(obj)
print(res)
{'name': 'Wes', 'pet': None, 'places_lived': ['United States', 'Spain', 'Germany'], 'siblings': [{'age': 30, 'name': 'Scott', 'pets': ['Zeus', 'Zuko']}, {'age': 38, 'name': 'Katie', 'pets': ['Sixes', 'Stache', 'Cisco']}]}
# 将python数据转换为json字符串
obj_1 = json.dumps(res)
print(obj_1)
{"name": "Wes", "pet": null, "places_lived": ["United States", "Spain", "Germany"], "siblings": [{"age": 30, "name": "Scott", "pets": ["Zeus", "Zuko"]}, {"age": 38, "name": "Katie", "pets": ["Sixes", "Stache", "Cisco"]}]}
sib = pd.DataFrame(res['siblings'],columns=['name','age'])
print(sib)
name age
0 Scott 30
1 Katie 38
二、数据清洗和准备
import pandas as pd
import numpy as np
data = pd.read_csv(r'guazi.csv')
data
leixing | nianfen | licheng | didian | shoujia | yuanjia | |
---|---|---|---|---|---|---|
0 | 凯迪拉克ATS-L 2016款 28T 时尚型 | 2016年 | 2.5万公里 | 长沙 | 16.77万 | 34.60万 |
1 | 奥迪A6L 2014款 TFSI 标准型 | 2014年 | 13.8万公里 | 长沙 | 21.96万 | 44.50万 |
2 | 本田 思域 2016款 1.8L 自动舒适版 | 2016年 | 4.8万公里 | 长沙 | 8.87万 | 15.20万 |
3 | 大众 朗逸 2015款 1.6L 自动舒适版 | 2016年 | 10.5万公里 | 长沙 | 7.27万 | 14.90万 |
4 | leixing | nianfen | licheng | didian | shoujia | yuanjia |
... | ... | ... | ... | ... | ... | ... |
2005 | 大众 途观 2013款 1.8TSI 自动两驱舒适版 | 2014年 | 7.3万公里 | 长沙 | 13.50万 | 25.80万 |
2006 | 现代ix35 2012款 2.0L 自动两驱精英版GLS | 2012年 | 7.1万公里 | 长沙 | 8.00万 | 21.30万 |
2007 | 宝马3系 2014款 320Li 时尚型 | 2015年 | 4.6万公里 | 长沙 | 23.00万 | 38.90万 |
2008 | 标致308 2014款 乐享版 经典 1.6L 手动优尚型 | 2015年 | 3.0万公里 | 长沙 | 6.20万 | 11.50万 |
2009 | 大众POLO 2014款 1.6L 自动舒适版 | 2016年 | 2.9万公里 | 长沙 | 7.40万 | 11.30万 |
2010 rows × 6 columns
2.1 处理缺失数据
方法 | 说明 |
---|---|
dropna | 根据各标签的值中是否存在缺失数据对轴标签进行过滤,可通过阈值调节对缺失值的容忍度 |
fillna | 用指定值或插值方法(如ffill或bfill)填充缺失数据 |
isnull | 返回之一含有布尔值的对象,这些布尔值表示哪些值是缺失值/NA,该对象的类型与源类型一样 |
notnull | isnull的否定式 |
data1 = pd.Series(['a','b',np.nan,'d'])
print(data1)
0 a
1 b
2 NaN
3 d
dtype: object
data2 = pd.DataFrame([[1.,6.5,3.],[1.,np.nan,np.nan],[np.nan,np.nan,np.nan],[np.nan,6.7,7.]])
print(data2)
0 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
2 NaN NaN NaN
3 NaN 6.7 7.0
2.1.1 判断缺失数据
print(data1.isnull(),'\n')
print(data2.isnull(),'\n')
0 False
1 False
2 True
3 False
dtype: bool
0 1 2
0 False False False
1 False True True
2 True True True
3 True False False
print(data1.isnull(),'\n')
print(data2.isnull(),'\n')
0 False
1 False
2 True
3 False
dtype: bool
0 1 2
0 False False False
1 False True True
2 True True True
3 True False False
2.1.2 滤除缺失数据
# 方法一:dropna 函数
print(data1.dropna())
0 a
1 b
3 d
dtype: object
# 方法二:使用条件索引
print(data1[data1.notnull()])
0 a
1 b
3 d
dtype: object
# 滤除dataframe数据
print(data2)
0 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
2 NaN NaN NaN
3 NaN 6.7 7.0
# 默认 axis=0 滤除包含NaN值的列数据
print(data2.dropna())
0 1 2
0 1.0 6.5 3.0
# 设置 how 参数为 all,可删除全为 NaN 值的行
print(data2.dropna(how='all'))
0 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
3 NaN 6.7 7.0
# axis=1,how='all' 删除列中全为NaN值的数据
print(data2.dropna(axis=1,how='all'))
0 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
2 NaN NaN NaN
3 NaN 6.7 7.0
df = pd.DataFrame(np.random.randn(7,3))
print(df)
0 1 2
0 0.915035 -0.558523 -0.197747
1 -0.375031 0.687483 -2.233406
2 -0.713800 -0.495835 -0.739050
3 0.939015 -1.675553 1.087504
4 -1.645904 -1.092061 -1.585963
5 0.248340 -0.513915 0.377761
6 0.288351 -0.275316 -0.744144
df.iloc[:4,1] = np.nan
df.iloc[:2,2] = np.nan
print(df)
0 1 2
0 0.915035 NaN NaN
1 -0.375031 NaN NaN
2 -0.713800 NaN -0.739050
3 0.939015 NaN 1.087504
4 -1.645904 -1.092061 -1.585963
5 0.248340 -0.513915 0.377761
6 0.288351 -0.275316 -0.744144
# thresh 参数可以设置删除某行(axis=0)或某列(axis=1)中NaN个数超过thresh的行或列数据
print(df.dropna(thresh=2))
0 1 2
2 -0.713800 NaN -0.739050
3 0.939015 NaN 1.087504
4 -1.645904 -1.092061 -1.585963
5 0.248340 -0.513915 0.377761
6 0.288351 -0.275316 -0.744144
2.1.3 填充数据
print(df,'\n')
0 1 2
0 0.915035 NaN NaN
1 -0.375031 NaN NaN
2 -0.713800 NaN -0.739050
3 0.939015 NaN 1.087504
4 -1.645904 -1.092061 -1.585963
5 0.248340 -0.513915 0.377761
6 0.288351 -0.275316 -0.744144
# fillna 函数,将所有NaN值填充为0
print(df.fillna(0))
0 1 2
0 0.915035 0.000000 0.000000
1 -0.375031 0.000000 0.000000
2 -0.713800 0.000000 -0.739050
3 0.939015 0.000000 1.087504
4 -1.645904 -1.092061 -1.585963
5 0.248340 -0.513915 0.377761
6 0.288351 -0.275316 -0.744144
# 传入字典类型参数可以将key指定的列数据上所有的NaN值替换为value值
# 如果想要本地修改则设置 inplace=True
print(df.fillna({1:0.9,2:0}))
0 1 2
0 0.915035 0.900000 0.000000
1 -0.375031 0.900000 0.000000
2 -0.713800 0.900000 -0.739050
3 0.939015 0.900000 1.087504
4 -1.645904 -1.092061 -1.585963
5 0.248340 -0.513915 0.377761
6 0.288351 -0.275316 -0.744144
df2 = pd.DataFrame(np.random.randn(6,3))
print(df2)
0 1 2
0 -0.895130 -0.221522 0.274133
1 -0.704247 -0.532311 0.684838
2 -1.280771 -1.754449 0.215026
3 1.981223 -0.025672 -1.022509
4 0.380020 0.358988 -1.011639
5 -0.873098 1.344166 -0.092629
df2.iloc[2:,1] = np.nan
df2.iloc[4:,2] = np.nan
print(df2)
0 1 2
0 -0.895130 -0.221522 0.274133
1 -0.704247 -0.532311 0.684838
2 -1.280771 NaN 0.215026
3 1.981223 NaN -1.022509
4 0.380020 NaN NaN
5 -0.873098 NaN NaN
# method='ffill' 使用邻近的非NaN填充NaN值
print(df2.fillna(method='ffill'))
0 1 2
0 -0.895130 -0.221522 0.274133
1 -0.704247 -0.532311 0.684838
2 -1.280771 -0.532311 0.215026
3 1.981223 -0.532311 -1.022509
4 0.380020 -0.532311 -1.022509
5 -0.873098 -0.532311 -1.022509
# limit=2 设置连续的NaN值中填充的个数
print(df2.fillna(method='ffill',limit=2))
0 1 2
0 -0.895130 -0.221522 0.274133
1 -0.704247 -0.532311 0.684838
2 -1.280771 -0.532311 0.215026
3 1.981223 -0.532311 -1.022509
4 0.380020 NaN -1.022509
5 -0.873098 NaN -1.022509
2.1.4 例子
print(data)
leixing nianfen licheng didian shoujia \
0 凯迪拉克ATS-L 2016款 28T 时尚型 2016年 2.5万公里 长沙 16.77万
1 奥迪A6L 2014款 TFSI 标准型 2014年 13.8万公里 长沙 21.96万
2 本田 思域 2016款 1.8L 自动舒适版 2016年 4.8万公里 长沙 8.87万
3 大众 朗逸 2015款 1.6L 自动舒适版 2016年 10.5万公里 长沙 7.27万
4 leixing nianfen licheng didian shoujia
... ... ... ... ... ...
2005 大众 途观 2013款 1.8TSI 自动两驱舒适版 2014年 7.3万公里 长沙 13.50万
2006 现代ix35 2012款 2.0L 自动两驱精英版GLS 2012年 7.1万公里 长沙 8.00万
2007 宝马3系 2014款 320Li 时尚型 2015年 4.6万公里 长沙 23.00万
2008 标致308 2014款 乐享版 经典 1.6L 手动优尚型 2015年 3.0万公里 长沙 6.20万
2009 大众POLO 2014款 1.6L 自动舒适版 2016年 2.9万公里 长沙 7.40万
yuanjia
0 34.60万
1 44.50万
2 15.20万
3 14.90万
4 yuanjia
... ...
2005 25.80万
2006 21.30万
2007 38.90万
2008 11.50万
2009 11.30万
[2010 rows x 6 columns]
print(data.fillna(0))
leixing nianfen licheng didian shoujia \
0 凯迪拉克ATS-L 2016款 28T 时尚型 2016年 2.5万公里 长沙 16.77万
1 奥迪A6L 2014款 TFSI 标准型 2014年 13.8万公里 长沙 21.96万
2 本田 思域 2016款 1.8L 自动舒适版 2016年 4.8万公里 长沙 8.87万
3 大众 朗逸 2015款 1.6L 自动舒适版 2016年 10.5万公里 长沙 7.27万
4 leixing nianfen licheng didian shoujia
... ... ... ... ... ...
2005 大众 途观 2013款 1.8TSI 自动两驱舒适版 2014年 7.3万公里 长沙 13.50万
2006 现代ix35 2012款 2.0L 自动两驱精英版GLS 2012年 7.1万公里 长沙 8.00万
2007 宝马3系 2014款 320Li 时尚型 2015年 4.6万公里 长沙 23.00万
2008 标致308 2014款 乐享版 经典 1.6L 手动优尚型 2015年 3.0万公里 长沙 6.20万
2009 大众POLO 2014款 1.6L 自动舒适版 2016年 2.9万公里 长沙 7.40万
yuanjia
0 34.60万
1 44.50万
2 15.20万
3 14.90万
4 yuanjia
... ...
2005 25.80万
2006 21.30万
2007 38.90万
2008 11.50万
2009 11.30万
[2010 rows x 6 columns]
2.2 数据转换
2.2.1 处理重复数据
data = pd.DataFrame({'k1':['one','two']*3+['two'],
'k2':[1,1,2,3,3,4,4]})
print(data)
k1 k2
0 one 1
1 two 1
2 one 2
3 two 3
4 one 3
5 two 4
6 two 4
- duplicated返回布尔型Series表示每行是否为重复行
# 根据前面是否出现过判断重复行
# 默认按照所有列的内容匹配重复
print(data.duplicated())
0 False
1 False
2 False
3 False
4 False
5 False
6 True
dtype: bool
# 传入list参数设置按照哪些列数据匹配重复
print(data.duplicated(['k1']))
0 False
1 False
2 True
3 True
4 True
5 True
6 True
dtype: bool
- drop_duplicates()过滤重复行
# 默认按照所有列的内容匹配重复
print(data.drop_duplicates())
k1 k2
0 one 1
1 two 1
2 one 2
3 two 3
4 one 3
5 two 4
# 传入list参数设置按照哪些列数据匹配重复
print(data.drop_duplicates(['k1']))
k1 k2
0 one 1
1 two 1
# 设置默认删除前面所有重复的值
print(data,'\n')
print(data.drop_duplicates(keep='last'))
k1 k2
0 one 1
1 two 1
2 one 2
3 two 3
4 one 3
5 two 4
6 two 4
k1 k2
0 one 1
1 two 1
2 one 2
3 two 3
4 one 3
6 two 4
2.2.2 利用函数或映射进行数据转换
data = pd.DataFrame({'food':['Apple','banana','orange','apple','Mango','tomato'],
'price':[4,3,3.5,6,12,3]})
print(data)
food price
0 Apple 4.0
1 banana 3.0
2 orange 3.5
3 apple 6.0
4 Mango 12.0
5 tomato 3.0
meat = {'apple':'fruit',
'banana':'fruit',
'orange':'fruit',
'mango':'fruit',
'tomato':'vagetables'}
# 按照某列的数值判断从属类别,并将信息新建到原数据列
data['class'] = data['food'].map(meat)
print(data)
food price class
0 Apple 4.0 NaN
1 banana 3.0 fruit
2 orange 3.5 fruit
3 apple 6.0 fruit
4 Mango 12.0 NaN
5 tomato 3.0 vagetables
# 使用映射函数
print(data,'\n') # 重置了data
data['class'] = data['food'].map(lambda x: meat.get(x.lower()))
print(data)
food price
0 Apple 4.0
1 banana 3.0
2 orange 3.5
3 apple 6.0
4 Mango 12.0
5 tomato 3.0
food price class
0 Apple 4.0 fruit
1 banana 3.0 fruit
2 orange 3.5 fruit
3 apple 6.0 fruit
4 Mango 12.0 fruit
5 tomato 3.0 vagetables
2.2.3 替换值
data = pd.Series([1,-999,2,-1000,3])
print(data)
0 1
1 -999
2 2
3 -1000
4 3
dtype: int64
# replace根据值的内容进行替换
# to_replace设置需要替换的数值列表
# value对应于to_replace的数值位置设置替换值
print(data.replace([-999,-1000],[np.nan,0]))
0 1.0
1 NaN
2 2.0
3 0.0
4 3.0
dtype: float64
# 使用字典参数可等效上述的两个参数
print(data.replace({-999:np.nan,-1000:0}))
0 1.0
1 NaN
2 2.0
3 0.0
4 3.0
dtype: float64
2.2.4 重命名轴索引
data = pd.DataFrame(np.arange(12).reshape((3,4)),
index=['Beijing','Tokyo','New York'],
columns=['one','two','three','four'])
print(data)
one two three four
Beijing 0 1 2 3
Tokyo 4 5 6 7
New York 8 9 10 11
# 重新索引(对原索引进行调整)
print(data.reindex(index=['Beijing','New York','Tokyo']),'\n')
# 由于没有与原索引对应,无法实现重命名的任务
print(data.reindex(index=[1,2,3]))
one two three four
Beijing 0 1 2 3
New York 8 9 10 11
Tokyo 4 5 6 7
one two three four
1 NaN NaN NaN NaN
2 NaN NaN NaN NaN
3 NaN NaN NaN NaN
# 方法一:直接替换 index 对象的值
# 大写
tran = lambda x:x[:4].upper()
print(data.index.map(tran),'\n')
# 替换原来的index
data.index = data.index.map(tran)
print(data)
Index(['BEIJ', 'TOKY', 'NEW '], dtype='object')
one two three four
BEIJ 0 1 2 3
TOKY 4 5 6 7
NEW 8 9 10 11
# 先重置 data
# 方法二:使用 rename 方法
print(data.rename(index=str.title,columns=str.upper))
ONE TWO THREE FOUR
Beijing 0 1 2 3
Tokyo 4 5 6 7
New York 8 9 10 11
# 结合字典型对象对标签更新
print(data.rename(index={'Tokyo':'东京'},columns={'three':'第三年'}))
one two 第三年 four
Beijing 0 1 2 3
东京 4 5 6 7
New York 8 9 10 11
2.2.5 离散化和面元划分
-
离散化:将连续的值变成离散值的操作
-
面元划分:将离散的值划定为指定区间的值(分阶段)
ages = [20,22,25,27,21,23,37,31,61,45,41,32]
# 面元 bin(划定区间)
bins = [18,25,35,60,100]
cuts = pd.cut(ages,bins)
# 将ages中的离散值转换为所属区间值
print(cuts)
[(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]]
Length: 12
Categories (4, interval[int64, right]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
# 返回 ages 各元素,所属 bins 区间的下标值
print(cuts.codes)
[0 0 0 1 0 0 2 1 3 2 2 1]
# 返回 ages 各元素,所属 bins 区间
print(cuts.categories)
IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]], dtype='interval[int64, right]')
# 统计区域划分后的各区间的值的个数
print(pd.value_counts(cuts))
(18, 25] 5
(25, 35] 3
(35, 60] 3
(60, 100] 1
dtype: int64
# 设置面元的开闭区间
cuts_1 = pd.cut(ages,bins,right=False)
print(cuts_1)
[[18, 25), [18, 25), [25, 35), [25, 35), [18, 25), ..., [25, 35), [60, 100), [35, 60), [35, 60), [25, 35)]
Length: 12
Categories (4, interval[int64, left]): [[18, 25) < [25, 35) < [35, 60) < [60, 100)]
# 命名区间
names = ['青年','年轻人','中年','老年']
cuts_2 = pd.cut(ages,bins,labels=names)
print(cuts_2)
['青年', '青年', '青年', '年轻人', '青年', ..., '年轻人', '老年', '中年', '中年', '年轻人']
Length: 12
Categories (4, object): ['青年' < '年轻人' < '中年' < '老年']
data = np.random.rand(20)
print(data)
[0.53456858 0.44578249 0.04042052 0.93446914 0.53793848 0.3494597
0.80669954 0.33582879 0.2548792 0.92986248 0.07492674 0.54910372
0.63209603 0.8102901 0.22497568 0.04927668 0.9396684 0.83759755
0.71066308 0.60853243]
# 设置 bins = 4,表示自动根据数值分布将其划分为4个区间
# precision=2 表示设置保留 2 位小数
data_1 = pd.cut(data,4,precision=2)
print(data_1)
[(0.49, 0.71], (0.27, 0.49], (0.04, 0.27], (0.71, 0.94], (0.49, 0.71], ..., (0.04, 0.27], (0.71, 0.94], (0.71, 0.94], (0.49, 0.71], (0.49, 0.71]]
Length: 20
Categories (4, interval[float64, right]): [(0.04, 0.27] < (0.27, 0.49] < (0.49, 0.71] < (0.71, 0.94]]
# qcut 函数
data = np.random.randn(1000)
print(data)
# qcut 自动划分包含等同个数的数的区间
cuts = pd.qcut(data,4)
print(cuts)
[(0.642, 4.048], (-0.726, -0.0328], (0.642, 4.048], (-3.568, -0.726], (0.642, 4.048], ..., (-0.0328, 0.642], (-0.0328, 0.642], (-0.0328, 0.642], (-0.726, -0.0328], (-0.726, -0.0328]]
Length: 1000
Categories (4, interval[float64, right]): [(-3.568, -0.726] < (-0.726, -0.0328] < (-0.0328, 0.642] < (0.642, 4.048]]
pd.value_counts(cuts)
(-3.568, -0.726] 250
(-0.726, -0.0328] 250
(-0.0328, 0.642] 250
(0.642, 4.048] 250
dtype: int64
# 设置自动划分各区间中包含数的个数的比例
cuts = pd.qcut(data,[0,0.1,0.5,0.9,1.])
print(cuts)
[(1.255, 4.048], (-1.336, -0.0328], (1.255, 4.048], (-1.336, -0.0328], (1.255, 4.048], ..., (-0.0328, 1.255], (-0.0328, 1.255], (-0.0328, 1.255], (-1.336, -0.0328], (-1.336, -0.0328]]
Length: 1000
Categories (4, interval[float64, right]): [(-3.568, -1.336] < (-1.336, -0.0328] < (-0.0328, 1.255] < (1.255, 4.048]]
# 按照 (0.1-0):(0.5-0.1):(0.9-0.5):(1.0-0.9) ---> 1:4:4:1 的比例从小到大划分区间
print(cuts.value_counts())
(-3.568, -1.336] 100
(-1.336, -0.0328] 400
(-0.0328, 1.255] 400
(1.255, 4.048] 100
dtype: int64
2.2.6 检测和过滤异常值
data = pd.DataFrame(np.random.randn(1000,4))
print(data)
0 1 2 3
0 0.058193 0.377252 -0.027805 -0.692871
1 -0.260613 0.703073 0.782897 1.065004
2 0.191974 -1.767126 0.944262 0.578397
3 1.075826 -0.696180 1.029438 -0.704189
4 1.262402 0.781030 -0.736455 0.184382
.. ... ... ... ...
995 -0.218156 1.184946 0.586725 0.718576
996 0.527347 -1.563576 -1.186479 -1.058085
997 1.265693 -0.585655 1.613620 -0.534025
998 -1.508731 0.287481 0.813883 -1.063898
999 0.890795 0.301612 1.200027 0.883201
[1000 rows x 4 columns]
print(data[(np.abs(data)>3).any(axis=1)])
0 1 2 3
24 3.269307 0.226283 0.620447 1.018873
247 0.263223 3.311795 0.136548 -0.583588
273 -0.078376 -0.159689 0.532239 -3.339462
379 -3.252917 -0.213957 -0.012385 -0.261206
510 -0.605521 1.211071 3.275973 0.522374
719 0.095083 3.432468 -0.490989 1.367091
727 -0.606702 -1.873455 -3.199690 0.290764
data[np.abs(data)>3] = 3
print(data.describe())
0 1 2 3
count 1000.000000 1000.000000 1000.000000 1000.000000
mean -0.030800 0.013330 0.030884 0.020129
std 1.003752 1.006792 0.989630 0.996250
min -2.936906 -2.931640 -2.786820 -2.997812
25% -0.747704 -0.634105 -0.618477 -0.651899
50% 0.019667 0.051025 0.034802 0.030156
75% 0.693021 0.660996 0.681150 0.666826
max 3.000000 3.000000 3.000000 3.000000
2.2.7 排列和随机采样
df = pd.DataFrame(np.arange(20).reshape((5,4)))
print(df)
0 1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
3 12 13 14 15
4 16 17 18 19
# 随机生成取自[0,6)不重复且打乱顺序的数组
sam = np.random.permutation(5)
print(sam)
[3 0 2 1 4]
# take 函数,沿着指定轴方向,按照新的index进行排列
print(df.take(sam),'\n')
# 等价于
print(df.reindex(index=sam))
0 1 2 3
3 12 13 14 15
0 0 1 2 3
2 8 9 10 11
1 4 5 6 7
4 16 17 18 19
0 1 2 3
3 12 13 14 15
0 0 1 2 3
2 8 9 10 11
1 4 5 6 7
4 16 17 18 19
# DataFrame 数据的随机采样
print(df.sample(n=3))
0 1 2 3
0 0 1 2 3
4 16 17 18 19
2 8 9 10 11
# Series 数据的随机采样
ch = pd.Series([5,6,1,6,3])
print(ch)
0 5
1 6
2 1
3 6
4 3
dtype: int64
# 采样个数超出原数据个数,则可使用 replace=True 设置可重复采样
print(ch.sample(n=10,replace=True))
2 1
4 3
1 6
0 5
0 5
1 6
4 3
4 3
3 6
0 5
dtype: int64
2.3 字符串操作
2.3.1 字符串方法
方法 | 说明 |
---|---|
count | 返回子串在字符串中的出现次数(非重叠) |
endswith、startwith | 如果字符串以某个后缀结果(以某个前缀开头),则返回True |
join | 将字符串用作连接其他字符串序列的分隔符 |
index | 如果在字符串中找到子串,则返回子串低于个字符所在的位置。如果没有找到,则引发ValueError |
find | 如果在字符串中找到子串,则返回第一个发现的子串的第一个字符所在的位置。如果没有找到,则返回 -1 |
rfind | 如果在字符串中找到子串,则返回最后一个发现的子串的第一个字符所在的位置。如果没有找到,则返回 -1 |
replace | 用另一个字符串替换指定子串 |
strip、rstrip、lstrip | 去除空白符(包括换行符)。相当于对各个元素执行 x.strip()(以及rstrip、lstrip) |
split | 通过指定的分隔符将字符串拆分为一组子串 |
lower、upper | 分别将字母字符转换为小写或大写 |
ljust、rjust | 用空格(或其他字符)填充字符串的空白侧以返回符合最低宽度的字符串 |
2.3.2 正则表达式
方法 | 说明 |
---|---|
findall、finditer | 返回字符串中所有的非重叠匹配模式。findall返回的是由所有模式组成的列表,而finditer则通过一个迭代器逐个返回 |
match | 从字符串起始位置匹配模式,还可以对模式各部分进行分组。如果匹配到模式,则返回一个匹配项对象,否则返回None |
search | 扫描整个字符串以匹配模式。如果找到则返回一个匹配项对象。跟match不同,其匹配项可以谓语字符串的任意位置,而不仅仅是起始位置 |
split | 根据找到的模式将字符串拆分为数段 |
sub、subn | 将字符串中的所有的(sub)或前 n 个(subn)模式替换为指定表达式。在替换字符串中可以通过\1、\2等符号表示各分组项 |
import re
text = 'foo bar\t bat \tqq'
print(re.split('\s+',text))
['foo', 'bar', 'bat', 'qq']
res = re.compile('\s+')
print(res.split(text))
['foo', 'bar', 'bat', 'qq']
print(res.findall(text))
[' ', '\t ', ' \t']
print(res.finditer(text))
<callable_iterator object at 0x0000017D0FC71588>
2.3.3 Pandas的矢量化字符串函数
data = {'a':'dave@qq.com','b':'steve@gmail.com',
'c':'sam@gmail.com','d':np.nan}
data = pd.Series(data)
print(data)
a dave@qq.com
b steve@gmail.com
c sam@gmail.com
d NaN
dtype: object
print(data.isnull())
a False
b False
c False
d True
dtype: bool
# 可使用 Series\DataFrame.str.XXX 对各元素执行相关处理
# 也可使用 Series ---> map DataFrame ---> applymap 对元素进行映射处理
data.str.contains('@')
a True
b True
c True
d NaN
dtype: object
2.4 总结
data = pd.read_csv('guazi.csv')
data
leixing | nianfen | licheng | didian | shoujia | yuanjia | |
---|---|---|---|---|---|---|
0 | 凯迪拉克ATS-L 2016款 28T 时尚型 | 2016年 | 2.5万公里 | 长沙 | 16.77万 | 34.60万 |
1 | 奥迪A6L 2014款 TFSI 标准型 | 2014年 | 13.8万公里 | 长沙 | 21.96万 | 44.50万 |
2 | 本田 思域 2016款 1.8L 自动舒适版 | 2016年 | 4.8万公里 | 长沙 | 8.87万 | 15.20万 |
3 | 大众 朗逸 2015款 1.6L 自动舒适版 | 2016年 | 10.5万公里 | 长沙 | 7.27万 | 14.90万 |
4 | leixing | nianfen | licheng | didian | shoujia | yuanjia |
... | ... | ... | ... | ... | ... | ... |
2005 | 大众 途观 2013款 1.8TSI 自动两驱舒适版 | 2014年 | 7.3万公里 | 长沙 | 13.50万 | 25.80万 |
2006 | 现代ix35 2012款 2.0L 自动两驱精英版GLS | 2012年 | 7.1万公里 | 长沙 | 8.00万 | 21.30万 |
2007 | 宝马3系 2014款 320Li 时尚型 | 2015年 | 4.6万公里 | 长沙 | 23.00万 | 38.90万 |
2008 | 标致308 2014款 乐享版 经典 1.6L 手动优尚型 | 2015年 | 3.0万公里 | 长沙 | 6.20万 | 11.50万 |
2009 | 大众POLO 2014款 1.6L 自动舒适版 | 2016年 | 2.9万公里 | 长沙 | 7.40万 | 11.30万 |
2010 rows × 6 columns
# step 1:处理缺失值
(data.isnull()).sum()
leixing 0
nianfen 0
licheng 0
didian 0
shoujia 0
yuanjia 73
dtype: int64
data.dropna(subset=['yuanjia'],inplace=True)
# step 2:删除异常值
data.drop([4,9],inplace=True)
data
leixing | nianfen | licheng | didian | shoujia | yuanjia | |
---|---|---|---|---|---|---|
0 | 凯迪拉克ATS-L 2016款 28T 时尚型 | 2016年 | 2.5万公里 | 长沙 | 16.77万 | 34.60万 |
1 | 奥迪A6L 2014款 TFSI 标准型 | 2014年 | 13.8万公里 | 长沙 | 21.96万 | 44.50万 |
2 | 本田 思域 2016款 1.8L 自动舒适版 | 2016年 | 4.8万公里 | 长沙 | 8.87万 | 15.20万 |
3 | 大众 朗逸 2015款 1.6L 自动舒适版 | 2016年 | 10.5万公里 | 长沙 | 7.27万 | 14.90万 |
5 | 凯迪拉克ATS-L 2016款 28T 时尚型 | 2016年 | 2.5万公里 | 长沙 | 16.77万 | 34.60万 |
... | ... | ... | ... | ... | ... | ... |
2005 | 大众 途观 2013款 1.8TSI 自动两驱舒适版 | 2014年 | 7.3万公里 | 长沙 | 13.50万 | 25.80万 |
2006 | 现代ix35 2012款 2.0L 自动两驱精英版GLS | 2012年 | 7.1万公里 | 长沙 | 8.00万 | 21.30万 |
2007 | 宝马3系 2014款 320Li 时尚型 | 2015年 | 4.6万公里 | 长沙 | 23.00万 | 38.90万 |
2008 | 标致308 2014款 乐享版 经典 1.6L 手动优尚型 | 2015年 | 3.0万公里 | 长沙 | 6.20万 | 11.50万 |
2009 | 大众POLO 2014款 1.6L 自动舒适版 | 2016年 | 2.9万公里 | 长沙 | 7.40万 | 11.30万 |
1935 rows × 6 columns
# step 2:处理重复值
(data.duplicated()).sum()
8
data.drop_duplicates(inplace=True)
(data.duplicated()).sum()
0
data
leixing | nianfen | licheng | didian | shoujia | yuanjia | |
---|---|---|---|---|---|---|
0 | 凯迪拉克ATS-L 2016款 28T 时尚型 | 2016年 | 2.5万公里 | 长沙 | 16.77 | 34.60万 |
1 | 奥迪A6L 2014款 TFSI 标准型 | 2014年 | 13.8万公里 | 长沙 | 21.96 | 44.50万 |
2 | 本田 思域 2016款 1.8L 自动舒适版 | 2016年 | 4.8万公里 | 长沙 | 8.87 | 15.20万 |
3 | 大众 朗逸 2015款 1.6L 自动舒适版 | 2016年 | 10.5万公里 | 长沙 | 7.27 | 14.90万 |
15 | Smart smart fortwo 2012款 1.0 MHD 硬顶标准版 | 2014年 | 5.6万公里 | 长沙 | 4.89 | 12.50万 |
... | ... | ... | ... | ... | ... | ... |
2005 | 大众 途观 2013款 1.8TSI 自动两驱舒适版 | 2014年 | 7.3万公里 | 长沙 | 13.50 | 25.80万 |
2006 | 现代ix35 2012款 2.0L 自动两驱精英版GLS | 2012年 | 7.1万公里 | 长沙 | 8.00 | 21.30万 |
2007 | 宝马3系 2014款 320Li 时尚型 | 2015年 | 4.6万公里 | 长沙 | 23.00 | 38.90万 |
2008 | 标致308 2014款 乐享版 经典 1.6L 手动优尚型 | 2015年 | 3.0万公里 | 长沙 | 6.20 | 11.50万 |
2009 | 大众POLO 2014款 1.6L 自动舒适版 | 2016年 | 2.9万公里 | 长沙 | 7.40 | 11.30万 |
1927 rows × 6 columns
# step 3 数据类型转换
data['shoujia'] = data['shoujia'].map(lambda x: float(x.replace('万','')))
data
leixing | nianfen | licheng | didian | shoujia | yuanjia | |
---|---|---|---|---|---|---|
0 | 凯迪拉克ATS-L 2016款 28T 时尚型 | 2016年 | 2.5万公里 | 长沙 | 16.77 | 34.60万 |
1 | 奥迪A6L 2014款 TFSI 标准型 | 2014年 | 13.8万公里 | 长沙 | 21.96 | 44.50万 |
2 | 本田 思域 2016款 1.8L 自动舒适版 | 2016年 | 4.8万公里 | 长沙 | 8.87 | 15.20万 |
3 | 大众 朗逸 2015款 1.6L 自动舒适版 | 2016年 | 10.5万公里 | 长沙 | 7.27 | 14.90万 |
15 | Smart smart fortwo 2012款 1.0 MHD 硬顶标准版 | 2014年 | 5.6万公里 | 长沙 | 4.89 | 12.50万 |
... | ... | ... | ... | ... | ... | ... |
2005 | 大众 途观 2013款 1.8TSI 自动两驱舒适版 | 2014年 | 7.3万公里 | 长沙 | 13.50 | 25.80万 |
2006 | 现代ix35 2012款 2.0L 自动两驱精英版GLS | 2012年 | 7.1万公里 | 长沙 | 8.00 | 21.30万 |
2007 | 宝马3系 2014款 320Li 时尚型 | 2015年 | 4.6万公里 | 长沙 | 23.00 | 38.90万 |
2008 | 标致308 2014款 乐享版 经典 1.6L 手动优尚型 | 2015年 | 3.0万公里 | 长沙 | 6.20 | 11.50万 |
2009 | 大众POLO 2014款 1.6L 自动舒适版 | 2016年 | 2.9万公里 | 长沙 | 7.40 | 11.30万 |
1927 rows × 6 columns
三、数据规整
3.1 层次化索引
import numpy as np
import pandas as pd
data = pd.Series(np.random.randn(9),index=[['a','a','a','b','b','c','c','d','d'],[1,2,3,1,3,1,2,2,3]])
print(data)
a 1 0.959344
2 2.139239
3 -1.087872
b 1 -0.622621
3 0.008807
c 1 -0.610277
2 -0.671484
d 2 1.298714
3 1.297636
dtype: float64
# 外层索引获取值
print(data['a'],'\n')
print(data.loc[['b','d']])
1 0.959344
2 2.139239
3 -1.087872
dtype: float64
b 1 -0.622621
3 0.008807
d 2 1.298714
3 1.297636
dtype: float64
# 忽略外层索引的内层索引选取
print(data.loc[:,2])
a 2.139239
c -0.671484
d 1.298714
dtype: float64
# 设置指定列数据为索引
frame = pd.DataFrame({'a':[0,1,2,3,4,5,6],'b':[7,6,5,4,3,2,1],
'c':['one','one','one','two','two','two','two'],
'd':[0,1,2,0,1,2,3]})
print(frame)
a b c d
0 0 7 one 0
1 1 6 one 1
2 2 5 one 2
3 3 4 two 0
4 4 3 two 1
5 5 2 two 2
6 6 1 two 3
3.1.1 set_index设置列数据作为索引
# 设置为外层索引的列数据需要先排序(即将同数值的排到一起)
# drop = False 表示不删除设置为索引值的列
frame2 = frame.set_index(['c','d'])
print(frame2)
a b
c d
one 0 0 7
1 1 6
2 2 5
two 0 3 4
1 4 3
2 5 2
3 6 1
3.1.2 reset_index将索引变为列数据
print(frame2.reset_index())
c d a b
0 one 0 0 7
1 one 1 1 6
2 one 2 2 5
3 two 0 3 4
4 two 1 4 3
5 two 2 5 2
6 two 3 6 1
3.2 数据连接
3.2.1 pd.merge 函数
-
pd.merge(left,right,how=‘inner’,on=None,left_on=None,right_on=None)
- left:合并时左边的DataFrame
- right:合并时右边的DataFrame
- how:合并的方式,默认 ‘inner’,‘outer’,‘left’,‘right’
- on:需要合并的列名,必须两边都有的列名,并以 left 和 right 中的列名的交集作为连接键
- left_on:left DataFrame 中用作连接键的列
- right_on:right DataFrame 中用作连接键的列
-
内连接 inner :对两张表都有的键的交集进行联合
-
全连接 outer :对两者表的都有的键的并集进行联合
-
左连接 left:对所有左表的键进行联合
-
右连接 right:对所有右表的键进行联合
left = pd.DataFrame({'key':['K0','K1','K2','K3'],
'A':['A0','A1','A2','A3'],
'B':['B0','B1','B2','B3']})
right = pd.DataFrame({'key':['K0','K0','K2','K3'],
'C':['C0','C1','C2','C3'],
'D':['D0','D1','D2','D3']})
print(left,'\n')
print(right,'\n')
key A B
0 K0 A0 B0
1 K1 A1 B1
2 K2 A2 B2
3 K3 A3 B3
key C D
0 K0 C0 D0
1 K0 C1 D1
2 K2 C2 D2
3 K3 C3 D3
# on='key' 设置在指定列索引上的连接
# how = 'outer' 设置全外连接
print(pd.merge(left,right,on='key',how='outer'))
key A B C D
0 K0 A0 B0 C0 D0
1 K0 A0 B0 C1 D1
2 K1 A1 B1 NaN NaN
3 K2 A2 B2 C2 D2
4 K3 A3 B3 C3 D3
- pd.merge 处理重复列名
# 处理重复列名
df_obj1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data' : np.random.randint(0,10,7)})
df_obj2 = pd.DataFrame({'key': ['a', 'b', 'd'],
'data' : np.random.randint(0,10,3)})
print(df_obj1,'\n')
print(df_obj2,'\n')
# suffixes 指定重复列索引名在合并后的添加后缀名
print(pd.merge(df_obj1, df_obj2, on='key', suffixes=('_left', '_right')))
key data
0 b 3
1 b 3
2 a 5
3 c 9
4 a 3
5 a 5
6 b 5
key data
0 a 7
1 b 1
2 d 3
key data_left data_right
0 b 3 1
1 b 3 1
2 b 5 1
3 a 5 7
4 a 3 7
5 a 5 7
- pd.merge 函数按索引连接
# 按索引连接
df_obj1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1' : np.random.randint(0,10,7)})
df_obj2 = pd.DataFrame({'data2' : np.random.randint(0,10,3)}, index=['a', 'b', 'd'])
print(df_obj1,'\n')
print(df_obj2,'\n')
# right_index = True,设置连接关键字为 key 的情况下,使用 “左列数据,右行索引”的连接方式
print(pd.merge(df_obj1, df_obj2, left_on='key', right_index=True))
key data1
0 b 8
1 b 0
2 a 8
3 c 3
4 a 5
5 a 1
6 b 0
data2
a 6
b 0
d 3
key data1 data2
0 b 8 0
1 b 0 0
6 b 0 0
2 a 8 6
4 a 5 6
5 a 1 6
left2 = pd.DataFrame(np.arange(1,7).reshape((3,2)),index=list('ace'),columns=['语文','数学'],dtype=np.float64)
right2 = pd.DataFrame(np.arange(7,15).reshape((4,2)),index=list('bcde'),columns=['英语','综合'],dtype=np.float64)
print(left2,'\n')
print(right2,'\n')
语文 数学
a 1.0 2.0
c 3.0 4.0
e 5.0 6.0
英语 综合
b 7.0 8.0
c 9.0 10.0
d 11.0 12.0
e 13.0 14.0
# 设置两边均为行索引作为连接关键字连接
print(pd.merge(left2,right2,how='outer',left_index=True,right_index=True))
语文 数学 英语 综合
a 1.0 2.0 NaN NaN
b NaN NaN 7.0 8.0
c 3.0 4.0 9.0 10.0
d NaN NaN 11.0 12.0
e 5.0 6.0 13.0 14.0
3.2.2 DataFrame.join 函数
print(left2,'\n')
print(right2,'\n')
语文 数学
a 1.0 2.0
c 3.0 4.0
e 5.0 6.0
英语 综合
b 7.0 8.0
c 9.0 10.0
d 11.0 12.0
e 13.0 14.0
# join使用两边的行索引作为连接关键字
# 注意:join方法不支持重复列索引名(即:两边存在相同的列索引名的合并)
print(left2.join(right2,how='outer'))
语文 数学 英语 综合
a 1.0 2.0 NaN NaN
b NaN NaN 7.0 8.0
c 3.0 4.0 9.0 10.0
d NaN NaN 11.0 12.0
e 5.0 6.0 13.0 14.0
3.2.3 pd.concat 函数
- Numpy 中的 concatenate 函数
arr1 = np.random.randint(0, 10, (3, 4))
arr2 = np.random.randint(0, 10, (3, 4))
print(arr1,'\n')
print(arr2,'\n')
print(np.concatenate([arr1, arr2]),'\n')
print(np.concatenate([arr1, arr2], axis=1))
[[0 6 0 0]
[2 5 7 5]
[9 9 7 6]]
[[2 7 4 8]
[9 2 3 0]
[0 2 1 3]]
[[0 6 0 0]
[2 5 7 5]
[9 9 7 6]
[2 7 4 8]
[9 2 3 0]
[0 2 1 3]]
[[0 6 0 0 2 7 4 8]
[2 5 7 5 9 2 3 0]
[9 9 7 6 0 2 1 3]]
- pd.concat
- 注意指定轴方向,默认 axis = 0
- join 指定合并方式,默认为outer
- Series 合并时查看行索引有无重复
df1 = pd.DataFrame(np.arange(6).reshape(3,2),index=list('abc'),columns=['one','two'])
df2 = pd.DataFrame(np.arange(4).reshape(2,2)+5,index=list('ac'),columns=['three','four'])
print(df1,'\n')
print(df2,'\n')
# 默认 axis = 0 是行方向上的数据合并,会自动匹配
# keys 可设置合并后的外层索引值
# 可用于两个DataFrame同列索引对象的数据合并
print(pd.concat([df1,df2],keys=['data1','data2'])) #默认外连接,axis=0
one two
a 0 1
b 2 3
c 4 5
three four
a 5 6
c 7 8
one two three four
data1 a 0.0 1.0 NaN NaN
b 2.0 3.0 NaN NaN
c 4.0 5.0 NaN NaN
data2 a NaN NaN 5.0 6.0
c NaN NaN 7.0 8.0
# 使用 行索引 作为连接关键字连接
print(pd.concat([df1,df2],axis=1)) #默认外连接,axis=1
# 等价于 pd.merge(df1,df2,how='outer',left_index=True,right_index=True)
one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
3.3 重塑和轴向旋转
3.3.1 重塑层次化索引
- stack
- 将列索引旋转为行索引,完成层级索引
- DataFrame —> Series
data = pd.DataFrame(np.arange(6).reshape((2,3)),
index=pd.Index(['老王','小刘'],name='姓名'),
columns=pd.Index(['语文','数学','英语'],name='科目'))
print(data)
科目 语文 数学 英语
姓名
老王 0 1 2
小刘 3 4 5
# 将行索引作为外层索引,列索引作为内层索引
s = data.stack()
print(s,'\n')
print(type(s))
姓名 科目
老王 语文 0
数学 1
英语 2
小刘 语文 3
数学 4
英语 5
dtype: int32
<class 'pandas.core.series.Series'>
- unstack
- 将层级索引展开
- Series —> DataFrame
- 默认操作内层索引,即level=-1
# 将层级索引转换为 DataFrame
print(s.unstack(level=-1),'\n')
# 等价于 print(s.unstack(level='科目'),'\n')
print(s.unstack(level=0),'\n') # 相当于转置
# 等价于 print(s.unstack(level='姓名'),'\n')
科目 语文 数学 英语
姓名
老王 0 1 2
小刘 3 4 5
姓名 老王 小刘
科目
语文 0 3
数学 1 4
英语 2 5
s1 = pd.Series([0,1,2,3],index=['a','b','c','d'])
s2 = pd.Series([4,5,6],index=['c','d','e'])
print(s1,'\n')
print(s2,'\n')
s = pd.concat([s1,s2],keys=['data1','data2'])
print(s)
a 0
b 1
c 2
d 3
dtype: int64
c 4
d 5
e 6
dtype: int64
data1 a 0
b 1
c 2
d 3
data2 c 4
d 5
e 6
dtype: int64
# 默认使用内层索引作为DataFrame的列索引,会填充nan值
print(s.unstack())
a b c d e
data1 0.0 1.0 2.0 3.0 NaN
data2 NaN NaN 4.0 5.0 6.0
# stack 方法默认会去除nan值
# 使用 dropna=False 设置不去除 NaN 值
print(s.unstack().stack(),'\n')
print(s.unstack().stack(dropna=False),'\n')
data1 a 0.0
b 1.0
c 2.0
d 3.0
data2 c 4.0
d 5.0
e 6.0
dtype: float64
data1 a 0.0
b 1.0
c 2.0
d 3.0
e NaN
data2 a NaN
b NaN
c 4.0
d 5.0
e 6.0
dtype: float64
3.3.2 轴向旋转
df3 = pd.DataFrame({'date':['2018-11-22','2018-11-22','2018-11-23','2018-11-23','2018-11-24'],
'class':['a','b','b','c','c'],
'values':[5,3,2,6,1]})
print(df3)
date class values
0 2018-11-22 a 5
1 2018-11-22 b 3
2 2018-11-23 b 2
3 2018-11-23 c 6
4 2018-11-24 c 1
df3.pivot('date','class','values')
class | a | b | c |
---|---|---|---|
date | |||
2018-11-22 | 5.0 | 3.0 | NaN |
2018-11-23 | NaN | 2.0 | 6.0 |
2018-11-24 | NaN | NaN | 1.0 |
四、数据分组和聚合
import pandas as pd
import numpy as np
df = pd.read_csv('starbucks_store_worldwide.csv')
df.head()
Brand | Store Number | Store Name | Ownership Type | Street Address | City | State/Province | Country | Postcode | Phone Number | Timezone | Longitude | Latitude | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Starbucks | 47370-257954 | Meritxell, 96 | Licensed | Av. Meritxell, 96 | Andorra la Vella | 7 | AD | AD500 | 376818720 | GMT+1:00 Europe/Andorra | 1.53 | 42.51 |
1 | Starbucks | 22331-212325 | Ajman Drive Thru | Licensed | 1 Street 69, Al Jarf | Ajman | AJ | AE | NaN | NaN | GMT+04:00 Asia/Dubai | 55.47 | 25.42 |
2 | Starbucks | 47089-256771 | Dana Mall | Licensed | Sheikh Khalifa Bin Zayed St. | Ajman | AJ | AE | NaN | NaN | GMT+04:00 Asia/Dubai | 55.47 | 25.39 |
3 | Starbucks | 22126-218024 | Twofour 54 | Licensed | Al Salam Street | Abu Dhabi | AZ | AE | NaN | NaN | GMT+04:00 Asia/Dubai | 54.38 | 24.48 |
4 | Starbucks | 17127-178586 | Al Ain Tower | Licensed | Khaldiya Area, Abu Dhabi Island | Abu Dhabi | AZ | AE | NaN | NaN | GMT+04:00 Asia/Dubai | 54.54 | 24.51 |
4.1 数据分组
- groupby(by=None,as_index=True)
- by:根据什么进行分组,用于确定groupby的分组
- as_index:对于聚合输出,返回以组便签为索引的对象,仅对DataFrame
df1 = pd.DataFrame({'fruit':['apple','banana','orange','apple','banana'],
'color':['red','yellow','yellow','cyan','cyan'],
'price':[8.5,6.8,5.6,7.8,6.4]})
print(df1)
fruit color price
0 apple red 8.5
1 banana yellow 6.8
2 orange yellow 5.6
3 apple cyan 7.8
4 banana cyan 6.4
# 返回一个 DataFrameGroupBy 可迭代对象
g = df1.groupby(by='fruit')
print(type(g))
<class 'pandas.core.groupby.generic.DataFrameGroupBy'>
for name,group in g:
print(name)
print(group)
print(type(group))
print('-'*10)
apple
fruit color price
0 apple red 8.5
3 apple cyan 7.8
<class 'pandas.core.frame.DataFrame'>
----------
banana
fruit color price
1 banana yellow 6.8
4 banana cyan 6.4
<class 'pandas.core.frame.DataFrame'>
----------
orange
fruit color price
2 orange yellow 5.6
<class 'pandas.core.frame.DataFrame'>
----------
# 选取任意的分组块
print(dict(list(g))['apple'])
fruit color price
0 apple red 8.5
3 apple cyan 7.8
4.2 聚合方法
函数名 | 描述 |
---|---|
count | 分组中非NA值的数量 |
sum | 非NA值的和 |
mean | 非NA值的平均值 |
median | 非NA值的中位数 |
std, var | 标准差和方差 |
min, max | 非NA的最小值,最大值 |
prod | 非NA值的乘积 |
first,last | 非NA值的第一个,最后一个 |
# 根据水果求价格的平均值
df1.groupby('fruit')['price'].mean()
# 或 df1['price'].groupby(df1['fruit']).mean()
fruit
apple 8.15
banana 6.60
orange 5.60
Name: price, dtype: float64
# as_index 设置返回的聚合数据中,自动新增行索引
df1.groupby('fruit',as_index=False)['price'].mean()
fruit | price | |
---|---|---|
0 | apple | 8.15 |
1 | banana | 6.60 |
2 | orange | 5.60 |
# 使用自定义聚合函数,我们需要将函数传递给agg或aggregate方法,
# 我们使用自定义聚合函数时,会比我们表中的聚合函数慢的多,因为要进行函数调用,数据重新排列
# 自定义聚合方法
def diff(arr):
return arr.max() - arr.min()
df1.groupby('fruit')['price'].agg(diff)
fruit
apple 0.7
banana 0.4
orange 0.0
Name: price, dtype: float64
# 层级索引下的分组与聚合
df1.groupby(['fruit','color'])['price'].mean()
fruit color
apple cyan 7.8
red 8.5
banana cyan 6.4
yellow 6.8
orange yellow 5.6
Name: price, dtype: float64