python数据分析之第三方模块Pandas

pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具。pandas提供了大量能使我们快速便捷地处理数据的函数和方法。你很快就会发现,它是使Python成为强大而高效的数据分析环境的重要因素之一。

Series

Series是一种类似与一维数组的对象,由下面两个部分组成:

  • values:一组数据(ndarray类型)
  • index:相关的数据索引标签

Series创建

列表创建

1
2
3
4
5
6
7
8
9
10
11
from pandas import Series

s = Series(data=[3, 4, 5, 2, 5])
print(s)

# 结果
0 3
1 4
2 5
3 2
4 5

numpy创建

1
2
3
4
5
6
7
8
9
10
11
12
13
import numpy as np
from pandas import Series
s = Series(data=np.random.randint(1, 50, size=6), index=['a', 'b', 'c', 'd', 'e', 'f'], name='test')
print(s)

# 结果
a 24
b 39
c 46
d 13
e 41
f 15
Name: test, dtype: int32

字典创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pandas import Series
dic = {
'name' : '张学友',
'gender' : '男',
'age' : 18,
'city' : '鹤岗'
}
s = Series(data=dic, name='test')
print(s)

# 结果
name 张学友
gender 男
age 18
city 鹤岗
Name: test, dtype: object

Series索引

可以使用中括号取单个索引(此时返回的是元素类型),或者中括号里一个列表取多个索引(此时返回的是一个Series类型)。

显式索引

  • 使用index中的元素作为索引值
  • 使用s.loc[](推荐):注意,loc中括号中放置的一定是显式索引
  • 使用s.索引元素直接获取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pandas import Series
dic = {
'name' : '张学友',
'gender' : '男',
'age' : 18,
'city' : '鹤岗'
}
s = Series(data=dic, name='test')
print(s['city'])
print(s.loc['city'])
print(s.city)

# 结果
鹤岗
鹤岗
鹤岗

隐式索引

  • 使用整数作为索引值
  • 使用.iloc[](推荐):iloc中的中括号中必须放置隐式索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pandas import Series
dic = {
'name' : '张学友',
'gender' : '男',
'age' : 18,
'city' : '鹤岗'
}
s = Series(data=dic, name='test')
print(s[3])
print(s.iloc[3])

# 结果
鹤岗
鹤岗

Series切片

显式索引切片

  • 直接使用索引区间:如s['a':'b']

  • 使用loc: 如s.loc['a':'b']

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pandas import Series
dic = {
'name' : '张学友',
'gender' : '男',
'age' : 18,
'city' : '鹤岗'
}
s = Series(data=dic, name='test')
print(s['gender':'city'])
print(s.loc['gender':'city'])

# 结果
gender 男
age 18
city 鹤岗
Name: test, dtype: object
gender 男
age 18
city 鹤岗
Name: test, dtype: object

隐式索引切片

  • 直接使用索引区间:如s[1:3]

  • 使用iloc: 如s.iloc[1:3]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pandas import Series
dic = {
'name' : '张学友',
'gender' : '男',
'age' : 18,
'city' : '鹤岗'
}
s = Series(data=dic, name='test')
print(s[0:2])
print(s.iloc[0:2])

# 结果
name 张学友
gender 男
Name: test, dtype: object
name 张学友
gender 男
Name: test, dtype: object

Series属性及基本使用

索引

1
2
3
4
5
6
7
8
9
10
11
12
from pandas import Series
dic = {
'name' : '张学友',
'gender' : '男',
'age' : 18,
'city' : '鹤岗'
}
s = Series(data=dic, name='test')
print(s.index)

# 结果
Index(['name', 'gender', 'age', 'city'], dtype='object')

1
2
3
4
5
6
7
8
9
10
11
12
from pandas import Series
dic = {
'name' : '张学友',
'gender' : '男',
'age' : 18,
'city' : '鹤岗'
}
s = Series(data=dic, name='test')
print(s.values)

# 结果
['张学友' '男' 18 '鹤岗']

head(n)

获取前n个值

1
2
3
4
5
print(s.head(2))
# 结果
name 张学友
gender 男
Name: test, dtype: object

tail(n)

获取后n个值

1
2
3
4
5
print(s.tail(2))
# 结果
age 18
city 鹤岗
Name: test, dtype: object

unique()

去重

1
2
3
4
5
s = Series(data=[1, 2, 3, 4, 5, 4, 3, 2, 3, 2])
print(s.unique())

# 结果
[1 2 3 4 5]

Series运算

数字运算

直接使用运算符+-*/进行运算

1
2
3
4
5
6
7
8
9
10
s = Series(data=[1, 2, 3, 4, 5])
print(s+10)

# 结果
0 11
1 12
2 13
3 14
4 15
dtype: int64
计算函数运算

使用add()sub()mul()div(),分别代表加减乘除。其中都可以携带参数,默认参数可以是数字或者Seriesfill_value代表如果元素为空则给其填充一个默认值

1
2
3
4
5
6
7
8
9
10
s = Series(data=[20, 30, None, 40, None], index=['a', 'b', 'c', 'd', 'e'])
print(s.add(10, fill_value=999))

# 结果
a 30.0
b 40.0
c 1009.0
d 50.0
e 1009.0
dtype: float64
多个series运算

当索引没有对应的值时,可能出现缺失数据显示NaN(not a number)的情况,使得两个Series进行相加:索引与之对应的元素会进行算数运算,不对应的就补空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
s1 = Series(data=[1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
s2 = Series(data=[10, 20, 30, 40], index=['f', 'c', 'e', 'a'])
print(s1+s2)
# 或者
s1.add(s2)

# 结果
a 41.0
b NaN
c 23.0
d NaN
e NaN
f NaN
dtype: float64

isnull()

检测元素是否为空

1
2
3
4
5
6
7
8
9
10
s = Series(data=[1, 2, None, 4, None], index=['a', 'b', 'c', 'd', 'e'])
print(s.isnull())

# 结果
a False
b False
c True
d False
e True
dtype: bool

notnull()

检测元素是否不为空

1
2
3
4
5
6
7
8
9
10
s = Series(data=[1, 2, None, 4, None], index=['a', 'b', 'c', 'd', 'e'])
print(s.notnull())

# 结果
a True
b True
c False
d True
e False
dtype: bool

DataFrame

DataFrame是一个【表格型】的数据结构。DataFrame由按一定顺序排列的多列数据组成。设计初衷是将Series的使用场景从一维拓展到多维。DataFrame既有行索引,也有列索引。

  • 行索引:index
  • 列索引:columns
  • 值:values

Dataframe创建

使用ndarray

1
DataFrame(data=np.random.randint(0,100,size=(5,6)))

运行结果:

012345
032930232140
127359764168
2639663309651
325028262641
432749784567

字典创建

1
2
3
4
5
dic = {
'张三':[77,88,99,90],
'李四':[67,88,99,78]
}
df = DataFrame(data=dic,index=['语文','数学','英语','理综'])

运行结果:

张三李四
语文7767
数学8888
英语9999
理综9078

Dataframe属性

values

元素值

1
2
3
4
5
6
dic = {
'张三':[77,88,99,90],
'李四':[67,88,99,78]
}
df = DataFrame(data=dic,index=['语文','数学','英语','理综'])
df.values

运行结果:

1
2
3
4
array([[77, 67],
[88, 88],
[99, 99],
[90, 78]], dtype=int64)

columns

列索引

1
df.columns

运行结果:

1
Index(['张三', '李四'], dtype='object')

index

行索引

1
df.index

运行结果:

1
Index(['语文', '数学', '英语', '理综'], dtype='object')

shape

形状、维度

1
df.shape

运行结果:

1
(4, 2)

DataFrame索引

列索引

字典方式

通过类似字典的方式取得如:df['a']

1
df['张三']

结果:

1
2
3
4
5
语文    77
数学 88
英语 99
理综 90
Name: 张三, dtype: int64
属性方式

通过类似对象取属性的方式取列,如:df.a

1
df.张三

结果:

1
2
3
4
5
语文    77
数学 88
英语 99
理综 90
Name: 张三, dtype: int64
取多列
1
df[['李四', '张三']]

结果:

李四张三
语文6777
数学8888
英语9999
理综7890
修改列索引
1
2
3
#修改列索引
df.columns = ['zhangsan','lisi']
df

结果:

zhangsanlisi
语文7767
数学8888
英语9999
理综9078

行索引

loc

使用.loc[]index来进行行索引

1
df.loc['语文']

结果:

1
2
3
张三    77
李四 67
Name: 语文, dtype: int64

选择多行

1
df.loc[['语文', '英语']]

结果:

张三李四
语文7767
英语9999
iloc

使用.iloc[]加整数来进行行索引(默认索引)

1
df.iloc[1]

结果:

1
2
3
张三    88
李四 88
Name: 数学, dtype: int64

多行索引

1
df.iloc[[1,3]]

结果:

张三李四
数学8888
理综9078

元素索引

行索引在前,列索引在后

loc

使用自定义索引获取,如:df.loc['理综', '李四']

1
df.loc['理综', '李四']

结果:

1
78
iloc

使用默认索引获取, 如:df.iloc[3, 1]

1
df.iloc[3, 1]

结果:

1
78

DataFrame切片

中括号切片

直接使用中括号,是按行进行切分的,且只能按行切分,如:df[0:2]

1
df[0:2]

结果:

张三李四
语文7767
数学8888
iloc

默认按行进行切分,第1个参数为行区间,第二个参数为列区间,且区间范围使用默认索引,如:df.iloc[1:2, 1:3]

只切行
1
df.iloc[0:2]

结果:

张三李四
语文7767
数学8888
只切列

默认第一个参数为行,如果只切列,行区间也要保留:

1
df.iloc[:, 1:2]

结果:

李四
语文67
数学88
英语99
理综78
切行切列
1
df.iloc[1:3, 1:2]

结果:

李四
数学88
英语99
loc

默认按行进行切分,第1个参数为行区间,第二个参数为列区间,且区间范围使用自定义索引,如:df.loc['语文':'英语', '张三':'李四']

只切行
1
df.loc['语文':'数学']

结果:

张三李四
语文7767
数学8888
只切列

默认第一个参数为行,如果只切列,行区间也要保留:

切单列

1
df.loc[:, '李四']

结果:

李四
语文67
数学88
英语99
理综78

切多列

1
2
3
4
5
6
7
8
dic = {
'张三':[77,88,99,90],
'李四':[67,92,97,68],
'王五':[83,79,91,78]
}
df = DataFrame(data=dic,index=['语文','数学','英语','理综'])

df.loc[:, '张三':'李四']

结果:

张三李四
语文7767
数学8892
英语9997
理综9068
切行切列
1
2
3
4
5
6
7
8
dic = {
'张三':[77,88,99,90],
'李四':[67,92,97,68],
'王五':[83,79,91,78]
}
df = DataFrame(data=dic,index=['语文','数学','英语','理综'])

df.loc['数学':'英语', '张三':'李四']

结果:

张三李四
数学8892
英语9997

DataFrame运算

DataFrame之间运算

Series一样:

  • 在运算中自动对齐不同索引的数据
  • 如果索引不对应,则补NaN
1
2
df1 = DataFrame(data=np.random.randint(1, 100, size=[4, 3]), index=['a', 'b', 'c', 'd'], columns=['aa', 'bb', 'cc'])
df2 = DataFrame(data=np.random.randint(1, 100, size=[4, 3]), index=['a', 'b', 'c', 'd'], columns=['bb', 'dd', 'aa'])

df1

aabbcc
a484844
b315324
c927110
d436240

df2:

bbddaa
a342260
b253021
c413613
d454850
1
df1+df2

结果为:

aabbccdd
a10882NaNNaN
b5278NaNNaN
c105112NaNNaN
d93107NaNNaN

下面以一个示例来说明DataFrame的运算,要求如下:

  1. 假设df1是期中考试成绩,df2是期末考试成绩,请自由创建df2,并将其与df1相加,求期中期末平均值。
  2. 假设张三期中考试数学被发现作弊,要记为0分,如何实现?
  3. 李四因为举报张三作弊立功,期中考试所有科目加100分,如何实现?
  4. 后来老师发现有一道题出错了,为了安抚学生情绪,给每位学生每个科目都加10分,如何实现?
1
2
3
4
5
# 防止每次变化
np.random.seed(2)
df1 = DataFrame(data=np.random.randint(80, 100, size=[4, 3]), index=['语文', '数学', '英语', '开车'], columns=['张三', '李四', '王五'])
np.random.seed(2)
df2 = DataFrame(data=np.random.randint(75, 100, size=[4, 3]), index=['语文', '数学', '英语', '开车'], columns=['张三', '李四', '王五'])

先看下两次考试结果,期中df1考试结果为:

张三李四王五
语文889593
数学889198
英语918887
开车829791

期末df2考试结果为:

张三李四王五
语文839088
数学839786
英语938683
开车827792

求平均值

1
2
# 期中加期末除以2
(df1+df2)/2

结果:

张三李四王五
语文85.592.590.5
数学85.594.092.0
英语92.087.085.0
开车82.087.091.5

张三期中数学0分

1
2
3
4
df1.loc['数学', '张三'] = 0
# 或者使用默认索引
# df1.iloc[1,0] = 0
df1

结果:

张三李四王五
语文889593
数学09198
英语918887
开车829791

李四期中所有加100

1
2
3
4
df1.loc[:, '李四'] += 100
# 使用默认索引
# df1.iloc[:, 1] += 100
df1

结果:

张三李四王五
语文8819593
数学019198
英语9118887
开车8219791

期中全部加10

1
2
df1 += 10
df1

结果:

张三李四王五
语文98205103
数学10201108
英语10119897
开车92207101

删除重复元素

使用duplicated()函数检测重复的行,返回元素为布尔类型的Series对象,每个元素对应一行,如果该行不是第一次出现,则元素为True

1
2
3
4
5
6
7
8
import numpy as np
from pandas import DataFrame, Series
#创建一个df
np.random.seed(2)
df = DataFrame(data=np.random.randint(1, 100, size=[7, 3]), index=['a', 'b', 'c', 'd', 'e', 'f', 'g'], columns=['aa', 'bb', 'cc'])
#手动将df的某几行设置成相同的内容
df.loc[['b', 'd', 'f']] = 888
print(df)

结果:

aabbcc
a411673
b888888888
c76835
d888888888
e864864
f888888888
g384068

使用duplicated查看所有重复元素行

1
2
3
# keep代表保留某些行,可选有first、last、False分别代表保留首行重复行、末行重复行,不保留任何重复行
duplicate_rows = df.duplicated(keep=False)
print(duplicate_rows)

结果:

1
2
3
4
5
6
7
8
a    False
b True
c False
d True
e False
f True
g False
dtype: bool

获取重复行的索引

1
2
duplicate_rows_index = df.loc[duplicate_rows].index
print(duplicate_rows_index)

结果:

1
Index(['b', 'd', 'f'], dtype='object')

根据重复行索引,删除行

1
2
# 根据索引删除行,axis=0代表行
df.drop(labels=duplicate_rows_index, axis=0)

结果:

aabbcc
a411673
c76835
e864864
g384068

使用drop_duplicates

df原始数据:

aabbcc
a411673
b888888888
c76835
d888888888
e864864
f888888888
g384068
1
2
# 不保留任何重复行
df.drop_duplicates(keep=False)

结果:

aabbcc
a411673
c76835
e864864
g384068

映射

replace()函数

替换元素-replace()函数:替换元素。使用replace()函数,对values进行映射操作

语法:

1
2
3
4
5
df.replace(
to_replace=None, # 要替换的值
value=None, # 替换后的值
inplace=False # 是否作用于原对象还是返回新对象
)
Series替换操作
  • 单值替换
    • 普通替换
    • 字典替换(推荐)
  • 多值替换
    • 列表替换
    • 字典替换(推荐)
  • 参数
    • to_replace:被替换的元素
DataFrame替换操作
  • 单值替换

    • 普通替换: 替换所有符合要求的元素:to_replace=15,value='e'
    • 按列指定单值替换: to_replace={列标签:替换值} value='value'
  • 多值替换

    • 列表替换: to_replace=[] value=[]
    • 字典替换(推荐) to_replace={to_replace:value,to_replace:value}
示例
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
>>> s = pd.Series([0, 1, 2, 3, 4])

0修改为5
>>> s.replace(0, 5)
0 5
1 1
2 2
3 3
4 4
dtype: int64

>>> df = pd.DataFrame({'A': [0, 1, 2, 3, 4],
... 'B': [5, 6, 7, 8, 9],
... 'C': ['a', 'b', 'c', 'd', 'e']})
0修改为5
>>> df.replace(0, 5)
A B C
0 5 5 a
1 1 6 b
2 2 7 c
3 3 8 d
4 4 9 e

**List-like `to_replace`**

0/1/2/3都修改为4
>>> df.replace([0, 1, 2, 3], 4)
A B C
0 4 5 a
1 4 6 b
2 4 7 c
3 4 8 d
4 4 9 e

0/1/2/3依次修改为4/3/2/1
>>> df.replace([0, 1, 2, 3], [4, 3, 2, 1])
A B C
0 4 5 a
1 3 6 b
2 2 7 c
3 1 8 d
4 4 9 e

1/2替换为所在元素下一行当前列的元素,bfill代表上一行当前列对应的元素
>>> s.replace([1, 2], method='bfill')
0 0
1 3
2 3
3 3
4 4
dtype: int64

**dict-like `to_replace`**
0替换为101替换为100
>>> df.replace({0: 10, 1: 100})
A B C
0 10 5 a
1 100 6 b
2 2 7 c
3 3 8 d
4 4 9 e
将A列中0和B列中5替换为100
>>> df.replace({'A': 0, 'B': 5}, 100)
A B C
0 100 100 a
1 1 6 b
2 2 7 c
3 3 8 d
4 4 9 e
将A列中的0替换为1004替换为400
>>> df.replace({'A': {0: 100, 4: 400}})
A B C
0 100 5 a
1 1 6 b
2 2 7 c
3 3 8 d
4 400 9 e

**Regular expression `to_replace`**

>>> df = pd.DataFrame({'A': ['bat', 'foo', 'bait'],
... 'B': ['abc', 'bar', 'xyz']})
将元素值ba开头且3位字母结束的值替换为new
>>> df.replace(to_replace=r'^ba.$', value='new', regex=True)
A B
0 new abc
1 foo new
2 bait xyz
将A列中元素值ba开头且3位字母结束的值替换为new
>>> df.replace({'A': r'^ba.$'}, {'A': 'new'}, regex=True)
A B
0 new abc
1 foo bar
2 bait xyz
将元素值ba开头且3位字母结束的值替换为new
>>> df.replace(regex=r'^ba.$', value='new')
A B
0 new abc
1 foo new
2 bait xyz
将元素值ba开头且3位字母结束的值替换为new,foo替换为xyz
>>> df.replace(regex={r'^ba.$': 'new', 'foo': 'xyz'})
A B
0 new abc
1 xyz new
2 bait xyz
将元素值ba开头且3位字母结束的值和职位foo的元素替换为new
>>> df.replace(regex=[r'^ba.$', 'foo'], value='new')
A B
0 new abc
1 new new
2 bait xyz

map()函数

新建一列 , map函数并不是df的方法,而是series的方法

  • map()可以映射新一列数据

  • map()中可以使用lambda表达式

  • map()中可以使用方法,可以是自定义的方法

    eg:map({to_replace:value})

  • 注意 map()中不能使用sum之类的函数,for循环;并不是任何形式的函数都可以作为map的参数。只有当一个函数具有一个参数且有返回值,那么该函数才可以作为map的参数。

示例
原始数据

人员对应工资表

1
2
3
4
5
6
7
8
np.random.seed(2)
dic={
'name':['JAY', 'TONY', 'DAVID'],
'salary': np.random.randint(9000, 500000, size=3)
}

df = DataFrame(data=dic)
df

结果:

namesalary
0JAY360400
1TONY109879
2DAVID212245
添加对应中文名
1
2
3
4
5
6
7
8
# 添加中文名
cname = {
'JAY' : '周杰伦',
'TONY': '托小尼',
'DAVID': '大卫'
}
df['cname'] = df['name'].map(cname)
df

结果:

namesalarycname
0JAY360400周杰伦
1TONY109879托小尼
2DAVID212245大卫
增加税后工资

工资超过20万,交20%的税,添加税后工资列

1
2
3
4
5
6
7
8
# 工资大于200000交20%税,税后工资
def calc_salary(salary):
if salary > 200000:
salary = salary - salary * 0.2
return salary

df['after_salary'] = df['salary'].map(calc_salary)
df

结果:

namesalarycnameafter_salary
0JAY360400周杰伦288320.0
1TONY109879托小尼109879.0
2DAVID212245大卫169796.0
匿名函数计算工资
1
2
df['after_salary1'] = df['salary'].map(lambda x: x - x * 0.2 if x>200000 else x)
df

结果:

namesalarycnameafter_salaryafter_salary1
0JAY360400周杰伦288320.0288320.0
1TONY109879托小尼109879.0109879.0
2DAVID212245大卫169796.0169796.0
Series map示例
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
# 创建Series
>>> s = pd.Series(['cat', 'dog', np.nan, 'rabbit'])
>>> s
0 cat
1 dog
2 NaN
3 rabbit
dtype: object


# 使用字典映射替换为对应的值,没有被字典中映射的值将赋为NaN
>>> s.map({'cat': 'kitten', 'dog': 'puppy'})
0 kitten
1 puppy
2 NaN
3 NaN
dtype: object

# 类似循环映射原始Series每个值到字符串
>>> s.map('I am a {}'.format)
0 I am a cat
1 I am a dog
2 I am a nan
3 I am a rabbit
dtype: object


# 如果na_action设置为ignore,将忽略该次映射关系
>>> s.map('I am a {}'.format, na_action='ignore')
0 I am a cat
1 I am a dog
2 NaN
3 I am a rabbit

异常值过滤

过滤掉以下DataFrame中cc列中元素小于cc列平均值的元素所在行

创建DataFrame
1
2
3
np.random.seed(3)
df = DataFrame(data=np.random.randint(1, 10, size=[5, 3]), columns=['aa', 'bb', 'cc'], index=['a', 'b', 'c', 'd', 'e'])
df

结果:

aabbcc
a949
b916
c468
d715
e892
检测平均值
1
2
3
4
5
6
# 过滤掉bb列元素小于当前列平均值的元素
# 计算cc列平均值,平均值为6.0
cc_avg = df['cc'].mean()
# 检测cc列元素不小于平均值
ret = df['cc']>=cc_avg
ret

结果:

1
2
3
4
5
6
a     True
b True
c True
d False
e False
Name: cc, dtype: bool
过滤结果
1
df.loc[ret]

结果:

aabbcc
a949
b916
c468

随机抽样

用于随机抽取其中的部分样本

take
  • take()函数接受一个索引列表,用数字表示,使得df根据列表中索引的顺序进行排序
  • eg:df.take([1,3,4,2,5])
原始数列
1
2
3
np.random.seed(2)
df = DataFrame(data=np.random.randint(1, 100, size=[20, 5]), columns=['a', 'b', 'c', 'd', 'e'])
df
abcde
04116732344
1837683550
29676864864
33291213840
4685435239
53459687089
66947719684
73267815377
8515916480
9504047951
10169182374
115891638497
12443327977
131141356110
147187712057
15832694182
166271981985
179188234453
187573919297
191756224494
随机抽取指定行数据
1
2
# axis=0代表行,随机抽取以下4行
df.take([19, 3, 9, 7], axis=0)

结果:

abcde
191756224494
33291213840
9504047951
73267815377
随机抽取指定列数据
1
2
# 抽取默认索引为4/2/0的列数据
df.take([4, 2, 0], axis=1)

结果:

abcde
187573919297
15832694182
04116732344
147187712057
29676864864
12443327977
131141356110
9504047951
179188234453
33291213840
4685435239
73267815377
191756224494
1837683550
115891638497
53459687089
10169182374
66947719684
8515916480
166271981985
随机打乱

DataFrame规模足够大时,直接使用np.random.permutation(x)函数,就配合take()函数实现随机抽样 ,np.random.permutation(x)生成从0到x-1为元素的列表

生成随机数列

1
rand_arr = np.random.permutation(20)

结果:

1
2
array([ 6,  7, 12, 17,  9,  8,  4, 18,  3,  1, 13, 15,  2, 10, 11,  5, 16,
0, 19, 14])

抽取随机后的所有数据

1
2
#行列全部打乱
df.take(rand_arr, axis=0).take(np.random.permutation(5), axis=1)

结果:

cdaeb
10182316749
9479505140
189192759773
12279447733
78153327767
4435268395
8916451805
07323414416
116384589791
1835835076
192244179456
133561111041
169819628571
147120715787
56870348959
67196698447
28648966476
15694183822
32138324091
172344915388

数据分类

数据聚合是数据处理的最后一步,通常是要使每一个数组生成一个单一的数值。

数据分类处理:

  • 分组:先把数据分为几组
  • 用函数处理:为不同组的数据应用不同的函数以转换数据
  • 合并:把不同组得到的结果合并起来

数据分类处理的核心:

1
2
3
- groupby()函数
- groups属性查看分组情况
- eg: df.groupby(by='item').groups

分组

原始数据

1
2
3
4
5
6
7
8
dic = {
'水果' : ['苹果', '梨','苹果', '草莓', '香蕉', '梨', '香蕉', '草莓'],
'价格' : [4, 3, 5.5, 12, 3.2, 3.5, 2.5, 10],
'颜色' : ['绿色', '绿色', '红色', '红色', '绿色', '黄色', '绿色', '黄色'],
'重量' : [12, 10, 9, 15, 19, 20, 17, 13]
}
df = DataFrame(data=dic)
df

结果:

水果价格颜色重量
0苹果4.0绿色12
13.0绿色10
2苹果5.5红色9
3草莓12.0红色15
4香蕉3.2绿色19
53.5黄色20
6香蕉2.5绿色17
7草莓10.0黄色13
根据水果类型分组
1
2
# axis=0表示列分组
df.groupby(by='水果', axis=0).groups

结果

1
2
3
4
{'梨': Int64Index([1, 5], dtype='int64'),
'苹果': Int64Index([0, 2], dtype='int64'),
'草莓': Int64Index([3, 7], dtype='int64'),
'香蕉': Int64Index([4, 6], dtype='int64')}
按颜色分组
1
2
# axis=0表示列分组
df.groupby(by='颜色', axis=0).groups

结果

1
2
3
{'红色': Int64Index([2, 3], dtype='int64'),
'绿色': Int64Index([0, 1, 4, 6], dtype='int64'),
'黄色': Int64Index([5, 7], dtype='int64')}

聚合

分组后要做的就是聚合统计数据

每种水果平均值
1
2
# 水果平均值,只有价格和重量列是数字可以计算
df.groupby(by='水果').mean()

结果

价格重量
水果
3.2515.0
苹果4.7510.5
草莓11.0014.0
香蕉2.8518.0
每种水果平均价格
1
2
df.groupby(by='水果')['价格'].mean()
# df.groupby(by='水果').mean()['价格']

结果

1
2
3
4
5
6
水果
3.25
苹果 4.75
草莓 11.00
香蕉 2.85
Name: 价格, dtype: float64
苹果的平均价格
1
2
3
4
5
# 先计算出每种水果平均价格
# 使用自定义索引获取
df.groupby(by='水果')['价格'].mean()['苹果']
# 使用默认索引获取苹果平均价
# df.groupby(by='水果')['价格'].mean()[1]
添加每个水果平均价格列
计算每种水果均价
1
2
3
#给df创建一个新列,内容为各个水果的平均价格
prices = df.groupby('水果')['价格'].mean()
prices

结果

1
2
3
4
5
6
水果
3.25
苹果 4.75
草莓 11.00
香蕉 2.85
Name: 价格, dtype: float64
每种水果价格平均价转换成字典
1
2
dic_prices = prices.to_dict()
dic_prices

结果:

1
{'梨': 3.25, '苹果': 4.75, '草莓': 11.0, '香蕉': 2.85}
使用map映射后添加到一个新列
1
2
df['平均价'] = df['水果'].map(dic_prices)
df

结果:

水果价格颜色重量平均价
0苹果4.0绿色124.75
13.0绿色103.25
2苹果5.5红色94.75
3草莓12.0红色1511.00
4香蕉3.2绿色192.85
53.5黄色203.25
6香蕉2.5绿色172.85
7草莓10.0黄色1311.00

高级数据聚合

使用groupby分组后,也可以使用transformapply提供自定义函数实现更多的运算

  • df.groupby('item')['price'].sum() <==> df.groupby('item')['price'].apply(sum)
  • transformapply都会进行运算,在transform或者apply中传入函数即可
  • transformapply也可以传入一个lambda表达式
使用apply计算每种水果平均价格
1
2
3
4
5
6
7
8
9
10
11
#计算平均价格
def calc_avg(s):
# 当前水果总价
sum = 0
# 循环当前该类水果所有商品,并将该类所有商品汇总
for i in s:
sum += i
# 将该类所有商品总价除以该类所有商品数量及该类商品平均价返回
return sum/s.size
avg_price = df.groupby(by='水果')['价格'].apply(calc_avg)
print(avg_price)

结果:

1
2
3
4
5
6
水果
梨 3.25
苹果 4.75
草莓 11.00
香蕉 2.85
Name: 价格, dtype: float64