Pandas

Pandas进阶及统计分析

基本数据对象及操作

数据清洗

数据合并及分组

透视表


基本数据对象及操作

  • 2008年由Wes McKinney创建
  • 一个强大的分析结构化数据的工具集
  • 基础是NumPy,提供了高性能矩阵的运算

链接:pandas

Series

  • 类似一维数组的对象
  • 通过list构建Series
    • ser_obj = pd.Series(range(10))
1
2
3
4
5
6
import pandas as pd

countries = ['中国', '美国', '澳大利亚']
countries_s = pd.Series(countries)
print(type(countries_s)) #series的数据类型
print(countries_s)

结果:

1
2
3
4
5
<class 'pandas.core.series.Series'>
0 中国
1 美国
2 澳大利亚
dtype: object
  • index对象
1
countries_s.index   #index对象

结果:

1
RangeIndex(start=0, stop=3, step=1)
  • 看series和array的关系
1
countries_s.values   #可以看出series和array的关系

结果:

1
array(['中国', '美国', '澳大利亚'], dtype=object)
  • 由数据和索引组成
    • 索引在左,数据在右
    • 索引是自动创建的
1
2
numbers = [4, 5, 6]
print(pd.Series(numbers)) #见下面结果 索引(index)0、1、2 是自动创建的

结果:

1
2
3
4
0    4
1 5
2 6
dtype: int64
  • 获取数据和索引
  • ser_obj.index , ser_obj.values
  • 预览数据
  • ser_obj.head(n)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
country_dicts = {'CH': '中国',
'US': '美国',
'AU': '澳大利亚'}

country_dict_s = pd.Series(country_dicts) #根据字典来创建series
# 给索引命名
country_dict_s.index.name = 'Code'
# 给数据命名
country_dict_s.name = 'Country'

print(country_dict_s)
print('-----------------------')
print(country_dict_s.values) #获取数据
print(country_dict_s.index) #获取索引
print('-----------------------')
print(country_dict_s)
print('-----------------------')
print(country_dict_s.head(2)) #预览前两行

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Code
AU 澳大利亚
CH 中国
US 美国
Name: Country, dtype: object
-----------------------
['澳大利亚' '中国' '美国']
Index(['AU', 'CH', 'US'], dtype='object', name='Code')
-----------------------
Code
AU 澳大利亚
CH 中国
US 美国
Name: Country, dtype: object
-----------------------
Code
AU 澳大利亚
CH 中国
Name: Country, dtype: object
  • 处理缺失数据
  • 如object—-> None, float—–>NaN
1
2
3
4
5
countries = ['中国', '美国', '澳大利亚', None]
print(pd.Series(countries))
print('-----------------------')
numbers = [4, 5, 6, None]
print(pd.Series(numbers))

结果:

1
2
3
4
5
6
7
8
9
10
11
0      中国
1 美国
2 澳大利亚
3 None
dtype: object
-----------------------
0 4.0
1 5.0
2 6.0
3 NaN
dtype: float64
  • Series索引数据
1
2
3
4
5
6
7
8
9
country_dicts = {'CH': '中国',
'US': '美国',
'AU': '澳大利亚'}

country_dict_s = pd.Series(country_dicts)
country_dict_s.name = 'Country'
country_dict_s.index.name = 'Code'

print(country_dict_s)

结果:

1
2
3
4
5
Code
AU 澳大利亚
CH 中国
US 美国
Name: Country, dtype: object
  • 通过索引判断数据是存在
1
2
3
4
# 通过索引判断数据是存在
# Series也可看作定长、有序的字典
print('CH' in country_dict_s)
print('NZ' in country_dict_s)

结果:

1
2
True
False
  • 几种获取数据的方法
1
2
3
4
5
6
7
8
9
10
#几种获取数据的方法
print('iloc:', country_dict_s.iloc[1]) #通过索引位置(整型数据)获取数据,ser_obj.iloc[idx]
print('loc:', country_dict_s.loc['US']) #通过索引名(字符串)获取数据,ser_obj['idx_name'],ser_obj.loc['idx_name']
print('[]:', country_dict_s['US'])
print('-----------------------------------')

#对series进行索引切片
print('iloc:\n', country_dict_s.iloc[ [0, 2] ])
print('--------------------------------------')
print('loc:\n', country_dict_s.loc[['US', 'AU']])

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
iloc: 中国
loc: 美国
[]: 美国
-----------------------------------
iloc:
Code
AU 澳大利亚
US 美国
Name: Country, dtype: object
--------------------------------------
loc:
Code
US 美国
AU 澳大利亚
Name: Country, dtype: object
  • 向量化操作
1
2
3
4
5
6
#通过时间的对比来观察向量化操作的优势
import numpy as np

s = pd.Series(np.random.randint(0, 1000, 10000))
print(s.head())
print(len(s))

结果:

1
2
3
4
5
6
7
0    610
1 718
2 331
3 496
4 429
dtype: int32
10000
  • 通过时间的对比,来突出向量化操作可以缩短时间
1
2
3
4
%%timeit -n 100        # %%只能在jyputer上使用  #对所有数据进行求和
total = 0
for item in s:
total += item

结果:

1
656 µs ± 63.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  • 对比上面循环操作,下方的整体操作可以大幅度缩小运行时间
1
2
%%timeit -n 100
total = np.sum(s)

结果:

1
149 µs ± 40.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

DataFrame

链接:dataframe

  • 类似多维数组/表格数据(如,excel,R中的data.frame)
  • 每列数据可以是不同的类型
  • 索引包括行索引(index)和列索引(label)
  • 创建Dataframe
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd

country1 = pd.Series({'Name': '中国',
'Language': 'Chinese',
'Area': '9.597M km2',
'Happiness Rank': 79})

country2 = pd.Series({'Name': '美国',
'Language': 'English (US)',
'Area': '9.834M km2',
'Happiness Rank': 14})

country3 = pd.Series({'Name': '澳大利亚',
'Language': 'English (AU)',
'Area': '7.692M km2',
'Happiness Rank': 9})

df = pd.DataFrame([country1, country2, country3], index=['CH', 'US', 'AU']) #通过dict构dataframe
  • 注意在jupyter中使用print和不使用print的区别
1
2
3
# 注意在jupyter中使用print和不使用print的区别
print(df)
df
  • 下面为print打印的结果
1
2
3
4
          Area  Happiness Rank      Language  Name
CH 9.597M km2 79 Chinese 中国
US 9.834M km2 14 English (US) 美国
AU 7.692M km2 9 English (AU) 澳大利亚
  • 下面为直接df后出来的图表

  • 添加值(values)
1
2
3
4
5
6
7
8
9
# 添加数据
# 如果个数小于要求的个数,会自动进行“广播”操作
# 如果大于要求的个数,会报错
df['Location'] = '地球'
print(df)

df['Region'] = ['亚洲', '北美洲', '大洋洲']
# print(df)
df

结果:

1
2
3
4
          Area  Happiness Rank      Language  Name Location
CH 9.597M km2 79 Chinese 中国 地球
US 9.834M km2 14 English (US) 美国 地球
AU 7.692M km2 9 English (AU) 澳大利亚 地球

  • Dataframe索引
    注意:从DataFrame中取出的数据进行操作后,会对原始数据产生影响。为了保证不对原始数据产生影响,应该使用copy()产生一个副本。在副本上进行操作。
1
2
3
4
5
6
7
# 行索引
print('loc:')
print(df.loc['CH'])
print(type(df.loc['CH']))
print('------------------------')
print('iloc:')
print(df.iloc[1])

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
loc:
Area 9.597M km2
Happiness Rank 79
Language Chinese
Name 中国
Location 地球
Region 亚洲
Name: CH, dtype: object
<class 'pandas.core.series.Series'>
------------------------
iloc:
Area 9.834M km2
Happiness Rank 14
Language English (US)
Name 美国
Location 地球
Region 北美洲
Name: US, dtype: object
  • 列索引
1
2
3
# 列索引
print(df['Area'])
print(type(df['Area']))

结果:

1
2
3
4
5
CH    9.597M km2
US 9.834M km2
AU 7.692M km2
Name: Area, dtype: object
<class 'pandas.core.series.Series'>
  • 获取不连续的列数据
1
2
# 获取不连续的列数据
print(df[['Name', 'Area']])

结果:

1
2
3
4
    Name        Area
CH 中国 9.597M km2
US 美国 9.834M km2
AU 澳大利亚 7.692M km2
  • 混合索引
1
2
3
4
5
6
7
8
9
10
# 混合索引
# 注意写法上的区别 #连锁性的数据访问
print('先取出列,再取行:')
print(df['Area']['CH'])
print(df['Area'].loc['CH'])
print(df['Area'].iloc[0])
print('---------------------------------')
print('先取出行,再取列:') #因为什么也不加,dataframe是默认先取列,所以df['CH']会报错,所以需要加loc或iloc
print(df.loc['CH']['Area'])
print(df.iloc[0]['Area'])

结果:

1
2
3
4
5
6
7
8
先取出列,再取行:
9.597M km2
9.597M km2
9.597M km2
---------------------------------
先取出行,再取列:
9.597M km2
9.597M km2
  • 转换行和列
1
2
# 转换行和列
print(df.T)

结果:

1
2
3
4
5
6
7
                        CH            US            AU
Area 9.597M km2 9.834M km2 7.692M km2
Happiness Rank 79 14 9
Language Chinese English (US) English (AU)
Name 中国 美国 澳大利亚
Location 地球 地球 地球
Region 亚洲 北美洲 大洋洲
  • 删除数据
1
2
3
4
5
6
7
8
print(df.drop(['CH']))
print('===================================')
# 注意drop操作只是将修改后的数据copy一份,而不会对原始数据进行修改
print(df)
print('===================================')
#修改
df_1 = df.drop(['CH'])
print(df_1)

结果:

1
2
3
4
5
6
7
8
9
10
11
12
          Area  Happiness Rank      Language  Name Location Region
US 9.834M km2 14 English (US) 美国 地球 北美洲
AU 7.692M km2 9 English (AU) 澳大利亚 地球 大洋洲
===================================
Area Happiness Rank Language Name Location Region
CH 9.597M km2 79 Chinese 中国 地球 亚洲
US 9.834M km2 14 English (US) 美国 地球 北美洲
AU 7.692M km2 9 English (AU) 澳大利亚 地球 大洋洲
===================================
Area Happiness Rank Language Name Location Region
US 9.834M km2 14 English (US) 美国 地球 北美洲
AU 7.692M km2 9 English (AU) 澳大利亚 地球 大洋洲
  • inplace的参数
1
2
3
print(df.drop(['CH'], inplace=True))
# 如果使用了inplace=True,会在原始数据上进行修改,同时不会返回一个copy,慎用
print(df)

结果:

1
2
3
          Area  Happiness Rank      Language  Name Location Region
US 9.834M km2 14 English (US) 美国 地球 北美洲
AU 7.692M km2 9 English (AU) 澳大利亚 地球 大洋洲
  • 删除列时,对axis的指定,0和1要区别(1是对列操作,0是对行操作)
1
2
3
#  如果需要删除列,需要指定axis=1
print(df.drop(['Area'], axis=1))
print(df)

结果:

1
2
3
4
5
6
    Happiness Rank      Language  Name Location Region
US 14 English (US) 美国 地球 北美洲
AU 9 English (AU) 澳大利亚 地球 大洋洲
Area Happiness Rank Language Name Location Region
US 9.834M km2 14 English (US) 美国 地球 北美洲
AU 7.692M km2 9 English (AU) 澳大利亚 地球 大洋洲
  • 也可以直接使用del关键字进行删除
1
2
3
# 也可直接使用del关键字
del df['Name']
print(df)

结果:

1
2
3
          Area  Happiness Rank      Language Location Region
US 9.834M km2 14 English (US) 地球 北美洲
AU 7.692M km2 9 English (AU) 地球 大洋洲

DataFrame的操作

1
df['Happiness Rank']

结果:

1
2
3
US    14
AU 9
Name: Happiness Rank, dtype: int64
  • 对取出的数据进行操作
1
2
3
4
5
# 注意从DataFrame中取出的数据进行操作后,会对原始数据产生影响 ###注意copy和不copy的区别
ranks = df['Happiness Rank']
ranks += 2
print(ranks)
print(df)

结果:

1
2
3
4
5
6
US    16
AU 11
Name: Happiness Rank, dtype: int64
Area Happiness Rank Language Location Region
US 9.834M km2 16 English (US) 地球 北美洲
AU 7.692M km2 11 English (AU) 地球 大洋洲
  • 对数据进行copy和非copy的之后的区别
1
2
3
4
5
6
# 注意从DataFrame中取出的数据进行操作后,会对原始数据产生影响
# 安全的操作是使用copy()
ranks = df['Happiness Rank'].copy()
ranks += 2
print(ranks)
print(df)

结果:

1
2
3
4
5
6
US    18
AU 13
Name: Happiness Rank, dtype: int64
Area Happiness Rank Language Location Region
US 9.834M km2 16 English (US) 地球 北美洲
AU 7.692M km2 11 English (AU) 地球 大洋洲

索引操作总结

  1. Pandas的索引可以归纳为3中:

  2. .loc,标签索引

  3. .iloc,位置索引 (loc与iloc主要用于行索引)

  4. .ix,标签与位置混合索引(先按标签索引尝试操作,然后再按位置索引尝试操作)

  5. 注意:

    1
    2
    1.  DataFrame索引时可将其看作ndarray操作
    2. 标签的切片索引是包含末尾位置的
  6. 数据读取

  7. pd.read_csv()

  8. index_col:指定索引列

  9. usecols:指定需要读取的列

  • 对于csv文档的基本操作
1
2
3
4
5
6
7
# 加载csv文件数据
report_2015_df = pd.read_csv('2015.csv')
print('2015年数据预览:')
#print(report_2015_df.head())
report_2015_df.head
report_2015_df.head(5)
report_2015_df.head() ##区别这三个的表达区别

1
print(report_2015_df.info())  #查看相关数据

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 158 entries, 0 to 157
Data columns (total 12 columns):
Country 158 non-null object
Region 158 non-null object
Happiness Rank 158 non-null int64
Happiness Score 158 non-null float64
Standard Error 158 non-null float64
Economy (GDP per Capita) 158 non-null float64
Family 158 non-null float64
Health (Life Expectancy) 158 non-null float64
Freedom 158 non-null float64
Trust (Government Corruption) 158 non-null float64
Generosity 158 non-null float64
Dystopia Residual 158 non-null float64
dtypes: float64(9), int64(1), object(2)
memory usage: 14.9+ KB
None
  • 对数据进行均值、方差等数据描述
1
report_2015_df.describe()  #对数据进行统计(每列)

  • 查看末尾的数据
1
report_2015_df.tail() #查看末尾的数据

索引对象Index

Series和DataFrame中的索引都是Index对象

  1. 不可变(保证了数据的安全)
  2. 常见的Index种类
    • Index
    • Int64Index
    • MultiIndex,‘层次’索引
    • DatetimeIndex,时间戳类型
  3. 重置索引 reset_index(),将索引重新赋值为0-1
  4. 重命名列名:df.rename(columns = {old_col:new_col},inplace = True)
1
2
3
4
5
6
7
# 使用index_col指定索引列
# 使用usecols指定需要读取的列
report_2016_df = pd.read_csv('./2016.csv',
index_col='Country',
usecols=['Country', 'Happiness Rank', 'Happiness Score', 'Region']) # ./是指本文件夹中
# 数据预览
report_2016_df.head()

  • 打印列名和行名
1
2
print('列名(column):', report_2016_df.columns)
print('行名(index):', report_2016_df.index)

结果:

1
2
3
4
5
6
7
列名(column): Index(['Region', 'Happiness Rank', 'Happiness Score'], dtype='object')
行名(index): Index(['Denmark', 'Switzerland', 'Iceland', 'Norway', 'Finland', 'Canada',
'Netherlands', 'New Zealand', 'Australia', 'Sweden',
...
'Madagascar', 'Tanzania', 'Liberia', 'Guinea', 'Rwanda', 'Benin',
'Afghanistan', 'Togo', 'Syria', 'Burundi'],
dtype='object', name='Country', length=157)
  • index是不可以变的
1
2
# 注意index是不可变的
# report_2016_df.index[0] = '丹麦' #**可以自己运行尝试一下,目前被注释掉
  • index的重置
1
2
3
# 重置index
# 注意inplace加与不加的区别
report_2016_df.reset_index(inplace=True)
1
report_2016_df.head()

  • 重命名列名
1
2
3
# 重命名列名
report_2016_df = report_2016_df.rename(columns={'Region': '地区', 'Hapiness Rank': '排名', 'Hapiness Score': '幸福指数'})
report_2016_df.head()

1
2
3
# 重命名列名,注意inplace的使用
report_2016_df.rename(columns={'Region': '地区', 'Happiness Rank': '排名', 'Happiness Score': '幸 福指数'},inplace=True)
report_2016_df.head()

  • 注意并且重要:轴的方向
    1. axis = 0 ,表示纵向计算(计算整行的数–每列每列)
    2. axis = 1 ,表示横向计算(计算整列的数–每行每行)

Boolean Mask

1
report_2016_df.head()

1
2
3
# 过滤 Western Europe 地区的国家
# only_western_europe = report_2016_df['地区'] == 'Western Europe'
report_2016_df[report_2016_df['地区'] == 'Western Europe']

  • 过滤数据
1
2
3
4
# 过滤 Western Europe 地区的国家
# 并且排名在10之外
only_western_europe_10 = (report_2016_df['地区'] == 'Western Europe') & (report_2016_df['排名'] > 10)
only_western_europe_10

结果:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
0      False
1 False
2 False
3 False
4 False
5 False
6 False
7 False
8 False
9 False
10 False
11 True
12 False
13 False
14 False
15 True
16 False
17 True
18 True
19 True
20 False
21 False
22 True
23 False
24 False
25 False
26 False
27 False
28 False
29 True
...
127 False
128 False
129 False
130 False
131 False
132 False
133 False
134 False
135 False
136 False
137 False
138 False
139 False
140 False
141 False
142 False
143 False
144 False
145 False
146 False
147 False
148 False
149 False
150 False
151 False
152 False
153 False
154 False
155 False
156 False
Length: 157, dtype: bool
  • 叠加boolean mask得到结果
1
2
# 叠加 boolean mask 得到最终结果
report_2016_df[only_western_europe_10]

1
2
# 熟练以后可以写在一行中
report_2016_df[(report_2016_df['地区'] == 'Western Europe') & (report_2016_df['排名'] > 10)]

层级索引

  • MulitIndex对象
  • set_index([‘a’,’b’],inplace = True),注意a,b的先后顺序
  • 选取子集
  • 外层选取ser_obj.loc[‘outer_index’]
  • 内层选取ser_obj.loc[‘out_index’,’inner_index’]
  • 常用于分组操作、透视表的生产等
  • 交换分层顺序
  • swaplevel()
  • 排序分层
  • sort_index(level = )
1
report_2015_df.head()

  • 设置层级索引
1
2
3
# 设置层级索引
report_2015_df2 = report_2015_df.set_index(['Region', 'Country','Happiness Rank']) #最好是赋给一个新的变量,保留原有的dataframe再进行操作
report_2015_df2.head(20)

1
2
# 只访西欧的国家,行索引,外层索引
report_2015_df2.loc['Western Europe']

  • 内层索引
1
2
# 内层索引
report_2015_df2.loc['Western Europe', 'Switzerland']

  • 交换分层顺序
1
2
# 交换分层顺序
report_2015_df2.swaplevel().head(10)

1
2
# 排序分层
report_2015_df2.sort_index(level=2).head(10) #level = 0 就是0级索引,最外层顺序索引

数据清洗

  • 是数据分析关键的一步,直接影响之后的处理工作
  • 数据该如何调整才能适用于接下来的分析和挖掘
  • 是一个迭代的过程,实际项目中可能需要不止一次地执行这些清洗操作

处理缺失数据

  • 判断数据缺失,ser.obj.isnull(),df_obj.insull(),相反操作为notnull()
  • 处理缺失数据
  • df.fillna(),df.dropna()
  • df.ffill(),按之前的数据填充
  • df.bfill(),按之后的数据填充
    -项目中使用ffill或bfill时,注意数据的排列顺序
1
2
3
4
import pandas as pd

log_data = pd.read_csv('log.csv')
log_data.head(10) #发现有很多空字符或者空值

  • 判断数据是否缺失
1
2
3
#判断数据是否缺失
log_data.info()
log_data.isnull().sum()

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 33 entries, 0 to 32
Data columns (total 6 columns):
time 33 non-null int64
user 33 non-null object
video 33 non-null object
playback position 33 non-null int64
paused 3 non-null object
volume 4 non-null float64
dtypes: float64(1), int64(2), object(3)
memory usage: 1.6+ KB

time 0
user 0
video 0
playback position 0
paused 30
volume 29
dtype: int64
  • 查看缺失值
1
log_data.isnull().head(10) #True表示缺失,False表示没有缺失

  • 判断某一列是否缺失
1
log_data['paused'].isnull().head(5) #判断某一列是否缺失

结果:

1
2
3
4
5
6
0    False
1 True
2 True
3 True
4 True
Name: paused, dtype: bool
  • 数据过滤处理
1
2
# 取出volume不为空的数据,数据过滤处理
log_data[log_data['volume'].notnull()]

  • 进行层级索引
1
2
3
log_data.set_index(['time', 'user'], inplace=True) #进行层级索引,先时间戳再user
log_data.sort_index(inplace=True) #进行排序
log_data.head(10)

  • 填充缺失值
1
log_data.fillna(0).head(10) #用0将缺失的值全部填充 #inplace = true就会让对原来数据产生影响

  • 删除空数据
1
log_data.dropna()  #删除空数据

  • 以上一个数据进行填充
1
log_data.ffill().head(10)  #按之前的数据填,指的就是上一个数据填

  • 按之后的数据进行填充
1
log_data.bfill().head(10)  #按之后的数据填充

数据变形

  • 处理重复数据
  • 判断数据是否重复,duplicated()
  • 去除重复数据,drop_duplicates(),可指定列及如何保留数据
  • 使用函数或map转化数据,通常根据字典进行数据转化
  • 替换值,replace()
  • 离散化和分箱操作,put.cut(),返回Categorical对象
  • 哑变量操作,pd.get_dummies()
  • 向量化字符串操作
  • 字符串列元素中是否包含子字符串,ser_obj.str.contains()
  • 字符串列切片操作,ser_obj.str[a:b]
1
2
3
4
#处理重复数据
data = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'],
'k2': [1, 1, 2, 3, 3, 4, 4]})
data

  • 判断数据是否重复
1
2
# 判断数据是否重复
data.duplicated()

结果:

1
2
3
4
5
6
7
8
0    False
1 False
2 False
3 False
4 False
5 False
6 True
dtype: bool
  • 去除重复数据
1
2
# 去除重复数据
data.drop_duplicates()

  • 若col索引不存在会自动添加列索引以及值
1
2
data['v1'] = range(7)  #如果col的索引不存在会自动添加列索引以及值
data

  • 去除列的重复数据
1
2
# 去除指定列的重复数据
data.drop_duplicates(['k1'])

  • 保持后面的那个数据
1
data.drop_duplicates(['k1', 'k2'], keep='last')  #通过两列去重复,keep指的是保持最后的那个数据

  • 使用函数或map转换数据
1
2
3
4
#使用函数或map转化数据
data = pd.DataFrame({'food': ['bacon', 'pulled pork', 'bacon', 'Pastrami', 'corned beef', 'Bacon', 'pastrami', 'honey ham', 'nova lox'],
'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})
data

  • 添加一列
1
2
3
4
5
6
7
8
9
# 添加一列,用于指定食物的来源
meat_to_animal = {
'bacon': 'pig',
'pulled pork': 'pig',
'pastrami': 'cow',
'corned beef': 'cow',
'honey ham': 'pig',
'nova lox': 'salmon'
}
1
2
3
4
# 使用map()
lowercased = data['food'].str.lower() #将food那列的数据的字符串全部转化为小写
data['animal'] = lowercased.map(meat_to_animal) # 添加一列animal并将其数据用map函数对应
data

  • 使用匿名函数
1
2
3
# 使用方法,使用lambda
data['animal2'] = data['food'].map(lambda x: meat_to_animal[x.lower()])
data

  • 用replace替换(将-999替换成为空值)
1
2
3
#替换值replace
data = pd.Series([1., -999., 2., -999., -1000., 3.])
data

结果:

1
2
3
4
5
6
7
0       1.0
1 -999.0
2 2.0
3 -999.0
4 -1000.0
5 3.0
dtype: float64
1
2
3
4
import numpy as np

# 将-999替换为空值
data.replace(-999, np.nan)

结果:

1
2
3
4
5
6
7
0       1.0
1 NaN
2 2.0
3 NaN
4 -1000.0
5 3.0
dtype: float64
  • 将-999,-1000都替换为空值
1
2
# 将-999,-1000都替换为空值
data.replace([-999, -1000], np.nan)

结果:

1
2
3
4
5
6
7
0    1.0
1 NaN
2 2.0
3 NaN
4 NaN
5 3.0
dtype: float64
  • 将-999,-1000分别替换为空值和0
1
2
# 将-999,-1000分别替换为空值和0
data.replace([-999, -1000], [np.nan, 0])

结果:

1
2
3
4
5
6
7
0    1.0
1 NaN
2 2.0
3 NaN
4 0.0
5 3.0
dtype: float64
  • 用字典来数值替换
1
2
#用字典来数值替换
data.replace({-999: np.nan, -1000: 0})

结果:

1
2
3
4
5
6
7
0    1.0
1 NaN
2 2.0
3 NaN
4 0.0
5 3.0
dtype: float64
  • 离散化和分箱操作
1
2
3
4
5
6
7
#离散化和分箱操作

# 年龄数据
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]

# 分箱的边界
bins = [18, 25, 35, 60, 100] ##将年龄数据分到边界之内 #将数据离散化可以用到pandas的分箱操作
1
2
cats = pd.cut(ages, bins)
print(type(cats))
1
<class 'pandas.core.categorical.Categorical'>
  • Categorical对象
1
2
# Categorical对象
cats

结果:

1
2
3
[(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]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
  • 获取分箱编码
1
2
# 获取分箱编码      #类别型特征处理的时候可以用分箱操作
cats.codes

结果:

1
array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)
  • 返回分箱边界索引
1
2
# 返回分箱边界索引
cats.categories

结果:

1
2
3
IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]]
closed='right',
dtype='interval[int64]')
  • 统计箱中元素的个数
1
2
# 统计箱中元素的个数
pd.value_counts(cats)

结果:

1
2
3
4
5
(18, 25]     5
(35, 60] 3
(25, 35] 3
(60, 100] 1
dtype: int64
  • 带标签的分箱
1
2
3
# 带标签的分箱  #将分箱用名字来代替,类别型的特征
group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
cats = pd.cut(ages, bins, labels=group_names)
1
cats.get_values()  #获取数据的值

结果:

1
2
3
array(['Youth', 'Youth', 'Youth', 'YoungAdult', 'Youth', 'Youth',
'MiddleAged', 'YoungAdult', 'Senior', 'MiddleAged', 'MiddleAged',
'YoungAdult'], dtype=object)
  • 统计箱中元素的个数
1
2
# 统计箱中元素的个数
pd.value_counts(cats) #对值进行统计

结果:

1
2
3
4
5
Youth         5
MiddleAged 3
YoungAdult 3
Senior 1
dtype: int64
  • 哑变量操作
1
2
3
4
#哑变量操作(sklearn里面是one-hot)
df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],
'data1': range(6)})
df

1
2
#哑变量
pd.get_dummies(df['key'])

  • 向量化操作
1
2
3
4
#向量化操作
data = {'Dave': 'dave@google.com', 'Steve': 'steve@gmail.com', 'Rob': 'rob@gmail.com', 'Wes': np.nan}
data = pd.Series(data)
data

结果:

1
2
3
4
5
Dave     dave@google.com
Rob rob@gmail.com
Steve steve@gmail.com
Wes NaN
dtype: object
  • 查看关键字
1
data.str.contains('gmail')  #关键字是否含有

结果:

1
2
3
4
5
Dave     False
Rob True
Steve True
Wes NaN
dtype: object
  • 取前5个数据
1
data.str[:5]  #取前5个数据

结果:

1
2
3
4
5
Dave     dave@
Rob rob@g
Steve steve
Wes NaN
dtype: object
  • split操作
1
2
split_df = data.str.split('@', expand=True)      #expand默认是false,如果是true就分成两列
split_df

  • 进行合并
1
split_df[0].str.cat(split_df[1], sep='@')  #cat是将两个合并,通过@字符

结果:

1
2
3
4
5
Dave     dave@google.com
Rob rob@gmail.com
Steve steve@gmail.com
Wes NaN
Name: 0, dtype: object

数据合并及分组

数据合并(pd.merge)

  1. 根据单个或多个键将不同DataFrame的行连接

  2. 默认将重叠列的列名作为‘外键’进行连接

    • on 显示指定’外键’
    • left_on ,左侧数据的’外键’
    • right_on,右侧数据的’外键’
  3. 默认是’内连接(inner),既结果中的键是交集

  4. how指定连接方式

  5. ‘外链接’(outer),结果中的键是并集

  6. ‘左连接’(left)

  7. ‘右连接’(right)

  8. 处理重复列名

  9. suffixes,默认为_x,_y

  10. 按索引连接

  11. left_index =True 或 right_index = True

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd

staff_df = pd.DataFrame([{'姓名': '张三', '部门': '研发部'},
{'姓名': '李四', '部门': '财务部'},
{'姓名': '赵六', '部门': '市场部'}])


student_df = pd.DataFrame([{'姓名': '张三', '专业': '计算机'},
{'姓名': '李四', '专业': '会计'},
{'姓名': '王五', '专业': '市场营销'}])

print(staff_df)
print()
print(student_df)

结果:

1
2
3
4
5
6
7
8
9
   姓名   部门
0 张三 研发部
1 李四 财务部
2 赵六 市场部

专业 姓名
0 计算机 张三
1 会计 李四
2 市场营销 王五
  • 并集
1
2
3
print(pd.merge(staff_df, student_df, how='outer', on = '姓名'))  ##并集
# 或者
staff_df.merge(student_df, how='outer', on='姓名')

结果:

1
2
3
4
5
   姓名   部门    专业
0 张三 研发部 计算机
1 李四 财务部 会计
2 赵六 市场部 NaN
3 王五 NaN 市场营销
  • 交集
1
2
3
print(pd.merge(staff_df, student_df, how='inner', on='姓名'))   #交集
# 或者
staff_df.merge(student_df, how='inner', on='姓名')

结果:

1
2
3
   姓名   部门   专业
0 张三 研发部 计算机
1 李四 财务部 会计
  • 左边数据是完整的
1
2
3
print(pd.merge(staff_df, student_df, how='left', on='姓名'))    #左边数据是完整的
# 或者
staff_df.merge(student_df, how='left', on='姓名')

结果:

1
2
3
4
   姓名   部门   专业
0 张三 研发部 计算机
1 李四 财务部 会计
2 赵六 市场部 NaN
  • 右边数据全完整
1
2
3
pd.merge(staff_df, student_df, how='right', on='姓名')  ##右边数据全有
# 或者
staff_df.merge(student_df, how='right', on='姓名')

  • 按索引进行合并
1
2
3
4
5
# 也可以按索引进行合并
staff_df.set_index('姓名', inplace=True)
student_df.set_index('姓名', inplace=True)
print(staff_df)
print(student_df)

结果:

1
2
3
4
5
6
7
8
9
10
     部门
姓名
张三 研发部
李四 财务部
赵六 市场部
专业
姓名
张三 计算机
李四 会计
王五 市场营销
  • 按第一个表格的索引合并
1
2
3
print(pd.merge(staff_df, student_df, how='left', left_index=True, right_index=True))
# 或者
staff_df.merge(student_df, how='left', left_index=True, right_index=True)

结果:

1
2
3
4
5
     部门   专业
姓名
张三 研发部 计算机
李四 财务部 会计
赵六 市场部 NaN
  • 当数据中的列名不同时,使用left_on,right_on
1
2
3
4
5
# 当数据中的列名不同时,使用left_on,right_on
staff_df.reset_index(inplace=True)
student_df.reset_index(inplace=True)
print(staff_df)
print(student_df)

结果:

1
2
3
4
5
6
7
8
   姓名   部门
0 张三 研发部
1 李四 财务部
2 赵六 市场部
姓名 专业
0 张三 计算机
1 李四 会计
2 王五 市场营销
1
2
3
4
staff_df.rename(columns={'姓名': '员工姓名'}, inplace=True)
student_df.rename(columns={'姓名': '学生姓名'}, inplace=True)
print(staff_df)
print(student_df)

结果:

1
2
3
4
5
6
7
8
  员工姓名   部门
0 张三 研发部
1 李四 财务部
2 赵六 市场部
学生姓名 专业
0 张三 计算机
1 李四 会计
2 王五 市场营销
  • 合并员工姓名和学生姓名
1
pd.merge(staff_df, student_df, how='left', left_on='员工姓名', right_on='学生姓名')  #合并员工姓名和学生姓名

  • 当两个数据包含相同的列名,合并后会给列名加后缀加以区别
1
2
3
4
5
# 如果两个数据中包含有相同的列名(不是要合并的列)时,merge会自动加后缀作为区别
staff_df['地址'] = ['天津', '北京', '上海']
student_df['地址'] = ['天津', '上海', '广州']
print(staff_df)
print(student_df)
1
2
3
4
5
6
7
8
  员工姓名   部门  地址
0 张三 研发部 天津
1 李四 财务部 北京
2 赵六 市场部 上海
学生姓名 专业 地址
0 张三 计算机 天津
1 李四 会计 上海
2 王五 市场营销 广州
1
pd.merge(staff_df, student_df, how='left', left_on='员工姓名', right_on='学生姓名') #会自动加后缀区别

  • 可以指定后缀名称
1
2
# 也可指定后缀名称
pd.merge(staff_df, student_df, how='left', left_on='员工姓名', right_on='学生姓名', suffixes=('(公司)', '(家乡)'))

  • 也可以指定多列进行合并,找出同一个人的工作地址和家乡地址相同的记录
1
2
# 也可以指定多列进行合并,找出同一个人的工作地址和家乡地址相同的记录
pd.merge(staff_df, student_df, how='inner', left_on=['员工姓名', '地址'], right_on=['学生姓名', '地址'])

函数应用

  • 可直接使用NumPy的ufunc函数,如abs等
  • 通过apply将函数应用到行或列上
  • 注意指定轴的方向,默认axis = 0
  • 通过applymap将函数应用到每个数据上
  • apply的使用场景比applymap要多
1
2
3
# apply使用
# 获取姓
staff_df['员工姓名'].apply(lambda x: x[0])

结果:

1
2
3
4
0
1
2
Name: 员工姓名, dtype: object
  • 获取名字
1
2
# 获取名
staff_df['员工姓名'].apply(lambda x: x[1:])

结果:

1
2
3
4
0    三
1 四
2 六
Name: 员工姓名, dtype: object
  • 结果合并
1
2
3
4
# 结果合并
staff_df.loc[:, '姓'] = staff_df['员工姓名'].apply(lambda x: x[0]) #:指行,逗号后面指列
staff_df.loc[:, '名'] = staff_df['员工姓名'].apply(lambda x: x[1:])
print(staff_df)

结果:

1
2
3
4
  员工姓名   部门  地址  姓  名
0 张三 研发部 天津 张 三
1 李四 财务部 北京 李 四
2 赵六 市场部 上海 赵 六

分组(groupby)

  • 对数据集进行分组,然后对每组进行统计分析
  • pandas能利用groupby进行更加复杂的分组运算
  • 分组运算
  • split > apply > combine
  • 拆分:进行分组的根据
  • 应用:每个分组云U型的计算规则
  • 合并:把每个分组的计算结果合并起来
1
2
3
#读取数据
report_data = pd.read_csv('./2015.csv')
report_data.head()

  • GroupBy对象:DataFrameGroupBy,SeriesGroupBy
  • GroupBy对象没有进行实际运算,只是包含分组的中间数据
  • 按列名分组,obj.groupby(‘label’)
  • 按列名多层分组,obj.groupby([‘label1’,’label2’]) —->多层dataframe
1
2
3
#groupby()
grouped = report_data.groupby('Region') #region这一列进行分组
print(type(grouped))

结果:

1
<class 'pandas.core.groupby.DataFrameGroupBy'>
  • 对GroupBy对象进行分组运算/多重分组运算,如mean()
  • 非数值数据不进行分组运算
1
grouped['Happiness Score'].mean() #分组里面,happiness的平均分

结果:

1
2
3
4
5
6
7
8
9
10
11
12
Region
Australia and New Zealand 7.285000
Central and Eastern Europe 5.332931
Eastern Asia 5.626167
Latin America and Caribbean 6.144682
Middle East and Northern Africa 5.406900
North America 7.273000
Southeastern Asia 5.317444
Southern Asia 4.580857
Sub-Saharan Africa 4.202800
Western Europe 6.689619
Name: Happiness Score, dtype: float64
  • size()返回每个分组的元素个数
1
grouped.size() #分组里面的数量,比如澳大利亚和新西兰组里面有2个

结果:

1
2
3
4
5
6
7
8
9
10
11
12
Region
Australia and New Zealand 2
Central and Eastern Europe 29
Eastern Asia 6
Latin America and Caribbean 22
Middle East and Northern Africa 20
North America 2
Southeastern Asia 9
Southern Asia 7
Sub-Saharan Africa 40
Western Europe 21
dtype: int64
  • Groupby对象支持迭代操作
  • 每次迭代返回一个元组(group_name,group_data)
  • 可用于分组数据的具体运算
1
2
3
4
5
6
7
# 迭代groupby对象
for group, frame in grouped: #group相当于index
mean_score = frame['Happiness Score'].mean()
max_score = frame['Happiness Score'].max()
min_score = frame['Happiness Score'].min()
print('{}地区的平均幸福指数:{},最高幸福指数:{},最低幸福指数{}\n'.format(group, mean_score,
max_score, min_score))

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Australia and New Zealand地区的平均幸福指数:7.285,最高幸福指数:7.2860000000000005,最低幸福指数7.284

Central and Eastern Europe地区的平均幸福指数:5.332931034482757,最高幸福指数:6.505,最低幸福指数4.218

Eastern Asia地区的平均幸福指数:5.626166666666666,最高幸福指数:6.297999999999999,最低幸福指数4.874

Latin America and Caribbean地区的平均幸福指数:6.1446818181818195,最高幸福指数:7.226,最低幸福指数4.518

Middle East and Northern Africa地区的平均幸福指数:5.406899999999999,最高幸福指数:7.278,最低幸福指数3.0060000000000002

North America地区的平均幸福指数:7.273,最高幸福指数:7.4270000000000005,最低幸福指数7.119

Southeastern Asia地区的平均幸福指数:5.317444444444444,最高幸福指数:6.797999999999999,最低幸福指数3.819

Southern Asia地区的平均幸福指数:4.580857142857143,最高幸福指数:5.252999999999999,最低幸福指数3.575

Sub-Saharan Africa地区的平均幸福指数:4.2028,最高幸福指数:5.477,最低幸福指数2.839

Western Europe地区的平均幸福指数:6.689619047619048,最高幸福指数:7.587000000000001,最低幸福指数4.857
  • 按自定义的函数分组
  • 如果自定义函数,操作针对的是index
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 自定义函数进行分组
# 按照幸福指数排名进行划分,1-10, 10-20, >20
# 如果自定义函数,操作针对的是index
report_data2 = report_data.set_index('Happiness Rank')
print(report_data2.head(5))
def get_rank_group(rank):
rank_group = ''
if rank <= 10:
rank_group = '0 -- 10'
elif rank <= 20:
rank_group = '10 -- 20'
else:
rank_group = '> 20'
return rank_group

grouped = report_data2.groupby(get_rank_group)
for group, frame in grouped:
print('{}分组的数据个数:{}'.format(group, len(frame)))

结果:

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
                    Country          Region  Happiness Score  Standard Error  \
Happiness Rank
1 Switzerland Western Europe 7.587 0.03411
2 Iceland Western Europe 7.561 0.04884
3 Denmark Western Europe 7.527 0.03328
4 Norway Western Europe 7.522 0.03880
5 Canada North America 7.427 0.03553

Economy (GDP per Capita) Family Health (Life Expectancy) \
Happiness Rank
1 1.39651 1.34951 0.94143
2 1.30232 1.40223 0.94784
3 1.32548 1.36058 0.87464
4 1.45900 1.33095 0.88521
5 1.32629 1.32261 0.90563

Freedom Trust (Government Corruption) Generosity \
Happiness Rank
1 0.66557 0.41978 0.29678
2 0.62877 0.14145 0.43630
3 0.64938 0.48357 0.34139
4 0.66973 0.36503 0.34699
5 0.63297 0.32957 0.45811

Dystopia Residual
Happiness Rank
1 2.51738
2 2.70201
3 2.49204
4 2.46531
5 2.45176
0 -- 10分组的数据个数:10
10 -- 20分组的数据个数:10
> 20分组的数据个数:138

实际项目中,通常可以先人为构造出一个分组列,然后再进行groupby

1
2
3
4
5
6
7
8
9
10
# 实际项目中,通常可以先人为构造出一个分组列,然后再进行groupby

# 按照score的整数部分进行分组
# 按照幸福指数排名进行划分,1-10, 10-20, >20
# 如果自定义函数,操作针对的是index
report_data['score group'] = report_data['Happiness Score'].apply(lambda score: int(score))

grouped = report_data.groupby('score group')
for group, frame in grouped:
print('幸福指数整数部分为{}的分组数据个数:{}'.format(group, len(frame)))

结果:

1
2
3
4
5
6
幸福指数整数部分为2的分组数据个数:2
幸福指数整数部分为3的分组数据个数:19
幸福指数整数部分为4的分组数据个数:44
幸福指数整数部分为5的分组数据个数:49
幸福指数整数部分为6的分组数据个数:29
幸福指数整数部分为7的分组数据个数:15

聚合(aggregation)

  • grouped.agg(func),数组产生标量的过程,如mean(),count()等
  • 常用于对分组后的数据进行计算
  • 内置的聚合函数:sum(),mean(),max(),min(),count(),size(),describe()
  • 可通过字典为每个列指定不同的操作方法
  • 可自定义函数,传入agg方法中
1
2
3
import numpy as np  

grouped.agg({'Happiness Score': np.mean, 'Happiness Rank': np.max})

  • 求各种数值(mean,最大值,最小值,方差)
1
grouped['Happiness Score'].agg([np.mean,np.amax,np.amin,np.std])

透视表

df.pivot_table(values,index,columns,aggfunc,margins)

  • values:透视表中的元素值(根据聚合函数得出的)
  • index:透视表的的行索引
  • columns:透视表的列索引
  • aggfunc:聚合函数,可以指定多个函数
  • margins:表示是否对所有数据进行统计
1
2
3
4
5
import pandas as pd
import numpy as np

cars_df = pd.read_csv('cars.csv')
cars_df.head()

  • 通过计算平均值来比较
1
2
# 我们想要比较不同年份的不同厂商的车,在电池方面的不同
cars_df.pivot_table(values='(kW)', index='YEAR', columns='Make', aggfunc=np.mean)

  • 通过多个聚合函数来对比
1
2
3
# 我们想要比较不同年份的不同厂商的车,在电池方面的不同
# 可以使用多个聚合函数
cars_df.pivot_table(values='(kW)', index='YEAR', columns='Make', aggfunc=[np.mean, np.min])

相关链接:源文件


-------------本文结束感谢您的阅读-------------


本文标题:Pandas

文章作者:HuXuzhe

发布时间:2018年07月09日 - 19:07

最后更新:2018年11月21日 - 16:11

原始链接:https://huxuzhe.github.io/2018/07/09/Pandas/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

HuXuzhe wechat
关注微信公众号:"AI数据分析算法",轻轻扫一扫,加入我们的AI_LEGENDS !
坚持原创,您的支持将鼓励我继续创作!
0%