极速入门Pandas数据分析

极速入门Pandas数据分析

数据载入

DataFrame

Pandas 有 Series 和 DataFrame 两种数据结构,对应一维数组和二维表,Series 可以理解成是一个只有一列的 DataFrame。

pd.DataFrame()可以直接创建 DataFrame 表,参数可以是字典或二维数组。使用字典创建表时,字典中的每个键值对代表一列数据,使用二维数组创建表时,一个内层数组代表一行数据。

创建 DataFrame 表时,还有一些常用的参数

  • index 指定每一行的 index,等于给每一行起个名字,类似 HBase 的行键,默认是从零开始的数字。
  • columns 指定列名,默认是从零开始的数字。

例如现在把下面这个数据载入成 DataFrame

NameAgeId
Ahay221001
Bronya241002
ZJN191004
# use dict
pd.DataFrame({
    "Name": ['Ahay', 'Bronya', 'ZJN'],
    "Age": [22, 24, 19],
    "Id": ['1001', '1002', '1003']
})
# or use list
pd.DataFrame([
    ["Ahay", 22, "1001"],
    ["Bronya", 24, "1002"],
    ["ZJN", 19, "1004"]
],columns=["Name","Age","Id"])

加载文件

读文件的函数都有个通用参数 encoding 用来指定文件编码

read_csv

从 csv 中读取文件,估计是最常用的吧,参数如下:

  • sep 指定字段的分隔符
  • names 指定表头,默认会用文件第一行做表头
  • header 指定文件第几行是表头,这行号也是从零开始数的,表头上面的内容会被忽略

DataFrame 操作

单元计算

DataFrame 与一个基本数据类型做计算,等于 DataFrame 中每个单元格对这个数据做计算

例如一个数据:

df = pd.DataFrame({
    "age":[22,23],
    "id":[1101,1102]
})

进行乘方运算df**2

   age       id
0  484  1212201
1  529  1214404

或进行比较df > 1000

     age    id
0  False  True
1  False  True

reindex

DataFrame 用 index 来表示每一行,默认 index 是从零开始数的行号

使用 reindex() 方法可以重定义 index,或在生成 DataFrame 时指定 index

在重定义 index 时,会移动已经存在的行的位置,不存在的行会填充为NaN,新 index 没有的行会被删除

df = pd.DataFrame({
    'value':[1,2,3]
},index=['a','c',2])

df.reindex(['a','b','c'])
#    value
# a    1.0  # 这一行没动
# b    NaN  # 这一行原来不存在,所以填充为NaN
# c    2.0  # 这一行原来存在,现在被移动到了这里

如果想让某个表的 index 和 columns 快速变为另一个表的样子,可以使用 reindex_like() 方法:

df = pd.DataFrame({
    'value': [1, 2, 3]
}, index=['a', 'c', 2])
newDf = pd.DataFrame({
    'value': [6, 7, 8]
})
newDf = newDf.reindex_like(df)
print(newDf)
#    value
# a    NaN  # 原来不存在
# c    NaN  # 原来不存在
# 2    8.0  # 原来存在(因为默认 index 是 0、1、2),值不变

默认情况下 reindex_like() 方法会同时重定义 index 和 columns

rename

rename() 可以给 DataFrame 的 index 或 columns 重命名,有两个参数分别是 columns 和 index,它们的值是字典,key 是原来的名字,value 是新名字。

# 把两个列名翻译成中文
df = df.rename(columns={
    "name":"姓名",
    "age":"年龄"
})

字典中不存在的 index 或 columns 不会被更改。

要区别于 reindex,reindex 会对表中数据产生影响,其实reindex 就是根据 index 对表重新整理。

而 rename 只是对表头改个名

DataFrame 数据选取

DataFrame 切片

列切片

DataFrame 的切片中第一个数据如果是一个字符串或一个包含字符串的列表,则为取列。

df["Id"]
df[["Name","Age"]]

只取一列且懒得写切片时,可以直接点取列:

df.Name
# 等同于
df['Name']

数据选取不会产生新数据,因此可以直接对原表进行修改

例如,把所有人名字改成 abc:

df['Name'] = 'abc'
loc 行切片

loc 的直接用法和列切片差不多,不过选择结果是行,所以切片里面不写 column 而是写 index

老数据:

df = pd.DataFrame({
    "name":["A","B","C","D"],
    "age":[22,23,26,23],
    "id":[1101,1102,1103,1104]
})

选择前两个数据df.loc[:1]

由于 loc 是按照 index 进行选择,index 不一定是数字,所以 loc 切片包含开头也包含结尾

  name  age    id
0    A   22  1101
1    B   23  1102

还有各种写法,这里就写个大概:

df.loc[[1,3]] # 选择第二行和第四行
df.loc[[1,3],['name']]# 选择第二行和第四行的name列,在赋值操作时这样用

df.loc[[1,3]]['name'] # 选择第二行和第四行的name列,结果只能查不能修改
iloc 行切片

loc 按照 index 选择行,但 index 并不是行号。

df = pd.DataFrame(
    [1,2,3,4,5],
    index=['a','b',1,2,3]
)
print(df.loc[2])
# 结果会显示数字4,因为 index 被修改了

所以还有个 iloc 按照行号选择行,例如输出前三行print(df.iloc[:3]),这个情况下用 loc 就可能出问题,因为 index 可能被修改过,导致 3 不一定指第四行。

因为是按照行号选择,就类似 python 数组的索引,所以 iloc 切片选择包含开头但不包含结尾

at、iat 单元选择

at 和 iat 的用法和 loc、iloc 一样,不过 at、iat 们的返回结果只是一个数据,不是一组,所以 at、iat 的切片中必须仅有两个元素来分别表示行和列,由此确定唯一的元素

df.at[4,'name'] # at 的写法更严格,只能取一个元素
# 等同于
df.loc[4,'name']

DataFrame 中的 Series

不论是上面的 loc 还是列切片,都有个奇怪的特性:切片中的第一个元素可以是一个列表也可以是单独的一个值。

例如df['name']df[['name']]都是正确且效果相同的

而且df[['name','age']]不能写成df['name','age']

这是因为他们的返回值不同

只取一列或一行的情况下,会得到一个 Series 列表,而取多行时得到的结果还是一个 DataFrame 表

type(df['name'])	# Series
type(df[['name']])	# DataFrame
type(df['name','age']) # 原地报错
type(df.loc[1])	# Series
type(df.loc[[1]])	# DataFrame
type(df.loc[1,'name']) # str 这样其实就是先取行再取列,所以结果是数据本身的类型
type(df.loc[[0,2,4],['name','age']]) # DataFrame 和上一行类似,先取0、2、4行,再取它们的列
type(df.loc[1,['name','age']]) # Series 只取了一行,所以是个 Series

可以看到通过控制切片中第一个元素数组还是数据,就可以控制返回结果是 Series 还是 DataFrame

DataFrame 筛选

在使用 loc 或直接对 DataFrame 切片时,如果切片中的第一个数据是一个包含布尔值的列表,则会按照这些布尔值选取对应的行。

例如还是这个数据集

df = pd.DataFrame({
    "name":["A","B","C","D"],
    "age":[22,23,26,23],
    "id":[1101,1102,1103,1104]
})

选择第二行和第四行df.loc[[False,True,False,True]]

  name  age    id
1    B   23  1102
3    D   23  1104

Series 也可以当做列表来用,因此利用 DataFrame 的列切片选取某一列后进行条件判断,可以快速生成一个与源数据有关的布尔值列表,再配合这个条件切片就可以实现类似 SQL 中 WHERE 的效果:

选择 age 大于25的数据df.loc[df['age'] > 25]

  name  age    id
2    C   26  1103

这个操作也可以直接对 DataFrame 进行,既不通过 loc 切片

df.loc[df['age'] > 25]
# 等同于
df[df['age'] > 25]

对筛选部分赋值

筛选后的结果可以进行多次切片,但只有筛选后不切片的结果才能赋值

# 把大于40岁的人员年龄设置为40
# 一次切片完成操作
df.loc[df['age'] > 40, 'age'] = 40

# 这样不行,因为在条件切片后面又对列切片了一次
df.loc[df['age'] > 40]['age'] = 40

逻辑运算

如果需要 andornot 这些逻辑操作,需要使用位运算符 &|~ 来表示。

如果是用来做筛选条件处理,这种操作只能对 Series 数据结构使用。

位运算符的优先级高于比较运算符,所以要记得对位运算符两边的表达式加括号。

(df['age'] > 20) & (df['age'] < 40) # 这两对括号必加

字符串处理

pandas 的字符串处理只针对 Series 数据结构有效,所以不能对多列或多行进行操作。

Series 结构有一个 str 属性提供多种字符串处理函数,例如:

  • contains() 是否能部分匹配正则
  • match() 是否能完全匹配正则
  • split() 分割字符串成列表
  • len() 长度
  • replace() 替换
  • extract() 使用正则提取数据,正则中使用括号包裹被提取的内容
  • get_dummies() 用分隔符分隔字符串,并用每一部分的值生成哑变量

基本上普通字符串有的这里都有

如果是简单的判断字符串相等的话不需要 str 直接 == 就行了

现在有一个测试表,有 id、姓名、性别、年龄 字段,包含三十条纪录,内容可以看文档最后的【测试用例】部分。

结合之前的内容,可以实现一些基础的查询功能:

# 读取csv文件,这句话以后就省略了
df = pd.read_csv(r"D:\笔记\Bigdata\pandas.csv", encoding='utf-8')

# 获取成年男性信息	[SQL] WHERE sex == ’男‘ and age >= 18
df[(df['sex'] == '男') & (df['age'] >= 18)]

# 获取名字中包含 雪 字的人员信息
df[(df['name'].str.contains('雪'))]

日期处理

Series 结构有一个用于日期处理的 dt 属性,用法和 str 一样,有以下属性:

  • second、hour、day等 快速获取时分秒日期等
  • tz_localize() 转换时区
  • strftime("%d %h这样的表达式") 格式化成字符串

此外,如果表中原有的日期列没有识别成日期数据类型,可以用 pandas 包下的 to_datetime 方法转换

df = pd.DataFrame({
    'name': "ABCDE",
    'time': ['20201212', '20201214', '20201211', '20201212', '20201217']
})
# 转换一下类型
df['time'] = pd.to_datetime(df['time'])
# 输出 12 日那天的数据
print(df[df['time'].dt.day == 12])
    name       time
0  ABCDE 2020-12-12
3  ABCDE 2020-12-12

query 快速查询

query 可以使用类Python原生语法快速查询数据,例如:

df.query('A < B')
# 查询结果等价于
df[df["A"] < df["B"]]

# 逻辑运算还用 not and or,判断等于依旧是双等于号
df.query('A > 40 and sex == "F"')
# 查询结果等价于
df[(df["A"] > 40) & (df["sex"] == "F")]

df.query("not (age > 12)")
# 查询结果等价于
df[~(df["age"] >12)]

这样的后果就是不能进行赋值操作了

删除

删除行

drop() 方法会返回删除某行数据后的 DataFrame,参数是被删除的 index

# 删除年龄大于 40 的
df.drop(df[df['age'] > 40].index)

如果希望使用原地算法删除,可以指定上 inplace = True 参数

删除列

列删除可以直接使用 del df[‘name’] 的形式,也可以给 drop() 指定 axis

del df['name'] # 删除 name 列

删除重复

drop_duplicates() 方法删除重复的内容,保留第一次出现的纪录,参数可以填列名或不写代表全部列

# 一共有几种名字
len(df['name'].drop_duplicates())
# 25

分组聚类

groupby 分组

对 DataFrame 使用 groupby() 进行分组,参数填上分组字段就行了,多个字段用列表表示。

分组后的结果不是 DataFrame 而是 DataFrameGroupBy,想获取数据需要调用一系列聚类函数:

  • mean() 平均值
  • count() 计数
  • sum() 求和
  • 等等等
# 不同性别的年龄平均值
df.groupby('sex')['age'].mean()

# 名字加性别的重复数,忽略不重复的名字
a = df.groupby(['sex', 'name'])['name'].count()
a[a > 1]
# sex  name
# 女   珍       3
#      芹       2
# 男   亮       2
#      平       2

如果不希望分组字段变成 index 可以指定 as_index=False

value_counts 快速计数

如果只是对某个字段分组计数,可以直接对 Series 使用 value_counts() 方法来省略 groupby()

# 不同性别的人数
df['sex'].value_counts()

排序

DataFrame 按字段排序

sort_values() 方法按照某些字段的值进行排序,返回排序后的 DataFrame

第一个参数是排序字段,可以是一个列名字符串或一个包含列名字符串的列表

ascending 参数指定是否升序排序,可以是一个布尔值,或是一个布尔值列表来表示每个排序字段的顺序

# 按年龄排序,从小到大,相同性别的放在一起
df = df.sort_values(['sex', 'age'], ascending=[False, True])
# 结果就这样
#      id name sex  age
# 9   517    豪   男    8
# 13  956    瑜   男   12
# 20  512    亮   男   15
# 27  796    鑫   女    8
# 7   479    童   女   15
# 21  887    莹   女   17

DataFrame 按 index 排序

sort_index() 按照 index 排序

# 给 df 换一个 index
df = df.reindex(range(29, -1, -1))
# 再按照 index 排序
df = df.sort_index()

Series 排序

Series 也支持 sort_values 和 sort_index ,其中 sort_values 就不用指定列名了

数据操作 API 函数

应用函数

pandas 可以对数据应用自定义函数,有以下几种方式

  • pipe(func) 表级操作,func 接受的参数是一个 DataFrame
  • apply(func,axis=0) 对 DataFrame 是行级操作,func 接受的参数是代表一列或一行的 Series ,对 groupby 结果操作则是组级操作,既 func 的参数是由每个组构成的 DataFrame
  • applymap(func) 元素级操作,对 Series 操作还可以用 map(),func 接受的参数就是单元格中的数值

在函数内部可以对元数据进行修改,除非指定了 raw=True,内部函数的返回值会被应用到一个位置对应的新 DataFrame

def func(x):
    return 1 if x == "男" else 0

# 把性别转换成1、0
df['sex'] = df['sex'].apply(func)

聚合应用函数

有两个针对 groupby 结构的应用函数:

  • agg(func) 聚合操作使用
  • transform(func) 分类转换操作使用
# 将年龄小于50的年龄更改为同性别的年龄总和
def transform(x):
    m = x.sum()
    x[x < 50] = m
    return x

df['age'] = df.groupby('sex')['age'].transform(transform)

# 去掉最高最低,求每个性别的平均年龄
def agg(x: pd.Series):
    x = x.drop(x.idxmin())
    x = x.drop(x.idxmax())
    return x.mean()

print(df.groupby('sex')['age'].agg(agg))

替换

map()replace() 方法可以替换单元中的内容。

  • map 只能对 Series 使用,它的参数是一个字典,key 是单元格中现有的值,value 是新的值,不在 key 中的值会被设为 NaN
  • replace 用法和一般的 replace 一样,一个参数表示原有值,第二个参数表示新值,其中这两个参数可以写成一个列表来一次替换多种数据

例如:

# 把 男 替换成 1,女 替换成 0
df['sex'] = df['sex'].map({'男': 1, '女': 0})
# 另一种写法
df['sex'] = df['sex'].replace(['男', '女'], [1, 0])
# 特征工程:工资这一列里的低中高替换成 0,1,2
df = df.replace({'工资': {'低': 0, '中': 1, '高': 2}})

map 的参数还可以是一个方法,在应用函数那一节有介绍

平移

shift() 函数可以让数据位置发生变化

df = pd.DataFrame([1, 2, 3], columns=['value'])
# 整体向后移动一下
df.shift(1)
#    value
# 0    NaN
# 1    1.0
# 2    2.0

利用 shift 可以实现连续重复数据剔除:

se = pd.Series([1, 2, 2, 2, 3, 3, 4, 2, 4, 4, 5, 8, 4])
se.drop(se[se.shift(1) == se].index)
# 1, 2, 3, 4, 2, 4, 5, 8, 4

统计函数

这些统计函数可以对 Series 结构做运算,大部分也能对 DataFrame 做运算,在 DataFrame 计算时就是把每一列当做 Series 做计算。

sum() 求和
median() 中位数
mean() 平均值
max() 最大值
**min() **最小值
idxmax() 最大值的 index
idxmin() 最小值的 index
cumsum() 累加,每个单元格的值等于这个单元格的值,再加上这个单元格上面全部单元格的值的和
mad() 平均绝对离差:
1 n ∑ i = 1 n ∣ x i − x ‾ ∣ \frac{1}{n} \sum^{n}_{i=1} |x_i - \overline{x}| n1i=1nxix
手算的话就是:(data - data.mean()).abs().mean()
std() 标准差,百度了一下发现标准差分总体标准差和样本标准差两种,std() 默认算的是样本标准差:
∑ i = 1 n ( x i − x ‾ ) 2 n − 1 \sqrt{\frac{ \sum^{n}_{i=1} (x_i - \overline{x})^2} { n-1 }} n1i=1n(xix)2
pandas 手算就是:

upper = ((data - data.mean()) ** 2).sum() # 分子
lower = (len(data) - 1) # 分母
print((upper / lower) ** 0.5) # 相除,开方

var() 方差,也分总体方差和样本方差,var() 默认也是样本方差:
∑ ( x − x ‾ ) 2 n − 1 \frac{\sum (x - \overline{x}) ^ 2}{n-1} n1(xx)2
pandas 手算:((data - data.mean())**2).sum() / (len(data) - 1)

diff() 一阶差分,就是每一个单元格相对于上一个单元格增加的量,第一个单元格值为NaN
pct_change() 百分数变化,和 diff() 类似,表示每一个单元格相对上一个单元格变化的百分比,第一个单元格值为NaN
corr() 计算列与列之间的相关性、相关性系数,如果是对 Series 计算,则需要指定其他 Series 作为比较对象,对 DataFrame 计算则会列出每一列之间的结果。这个东西的结果表示两列数据的相关程度,越接近相同的数据计算结果越接近 1,符号相反但绝对值相同的数据计算结果会是-1,毫无关联的两组数据结果会接近0
first() 获取第一行,配合 groupby 可以获得每组的第一个,在 groupby 前面 sort_values 可以获得每组的最值

最值

使用 max() 或 min() 取最大最小值,使用 nlargest() 或 nsmallest() 取指定数量的最值,这几个函数可以对 groupby结果、DataFrame、Series 使用

# 不同性别中最年长的人
df.groupby('sex').max()
#       id name  age
# sex               
# 女    954    香   73
# 男    956    豪   65

# id 前 10 的人员信息
df.loc[df['id'].nsmallest(10).index]
# 等同于
df.nsmallest(10, columns='id')
#      id name sex  age
# 19  356    芹   女   58
# 6   369    珍   女   73
# 16  424    磊   男   31

还有 idxmax()idxmin() 两个函数可以获取最大值和最小值所在的 index。

分段

pd.cut() 函数可以对某一个 Series 进行分段操作,返回一个 index + 区间范围 的 Series :

cut = pd.cut(df['age'], [0, 18, 35, 60, 120])
# 0      (18, 35]
# 1      (35, 60]
# 2      (18, 35]
# 3       (0, 18]
# ......
# 不同年龄段的人数
df.groupby(cut).count()['id']
# (0, 18]       8
# (18, 35]      6
# (35, 60]     13
# (60, 120]     3

小坑,cut 函数不在 Series 类里面,而是在 Pandas 包下面

窗口

rolling() 来对元数据进行滑动窗口的数值计算

d = pd.Series([1, 10, 100, 1000, 10000])
print(d.rolling(3).sum())
# 0            nan
# 1            nan
# 2     111.000000
# 3    1110.000000
# 4   11100.000000

还可以指定一个 center=True 参数让窗口从中间向两侧延伸

透视表 pivot_table

DataFrame 使用 pivot_table() 函数生成透视表,参数如下:

  • index 指定哪些列作为 index
  • values 指定那些列会被作为结果
  • aggfunc 对数据的聚合函数,sum、mean、count 等
  • columns 把某个字段中的每一个值挪到 columns 上,
  • margins 是否在表格末行末列添加汇总
  • fill_value 将空值填充为这个参数
# 统计不同性别人数
df.pivot_table(index=['sex'], aggfunc='count', values='name')
#      name
# sex      
# 女      18
# 男      12

奇怪的东西

一些目前还无法理解或不明所以的东西

  • DataFrame 支持迭代,迭代的结果是表头,和 df.columns 好像没啥区别

准备整理的东西

duplicated() 查看是否重复,以及keep参数

keep{‘first’, ‘last’, False}, default ‘first’

Determines which duplicates (if any) to mark.

  • first : Mark duplicates as True except for the first occurrence.
  • last : Mark duplicates as True except for the last occurrence.
  • False : Mark all duplicates as True.

any() 列中出现true就整列是true,相反的是all(),配合T转置可以实现某行是否存在空值

astype(‘int’) 转换类型

fillna() 空值填充,以及各种填充方式

join() 根据index连表

merge() 根据字段连表

read_sql()

pd.concat() 横向拼接表

np.where()

In [13]: df['logic'] = np.where(df['AAA'] > 5, 'high', 'low')

In [14]: df
Out[14]: 
   AAA  BBB  CCC logic
0    4   10  100   low
1    5   20   50   low
2    6   30  -30  high
3    7   40  -50  high
# 找到 AAA 每一组中 BBB 最小的哪一行
df.loc[df.groupby("AAA")["BBB"].idxmin()]

set_index()

stack() 堆叠展示数据,把每行展示成这样

# 元数据
	id name age
0	1  aaa  22
1	2  bbb  24

stack()之后就是
0	id	1
	name	aaa
	age	22
1	id	2
	name	bbb
	age 24
最左边是 index,然后是每个列名和值

reset_index()

get_group() 获取 goroupby 结果中的某个组,例如class为[1,2,3,4]的DataFrame df

df.groupby('class').get_group(2) # 获得由 class 为 2 这一组数据构成的 DataFrame
df.expanding() 这是个啥

pd.get_dummies()

merge、concat、duplicated实现差集并集交集计算

寻找异常值,对数值列进行Min、max、mean、std、var检查

可视化时对比性强的数据画成并联柱状图或者多条折线图最好

unique() 对 Series 不重复提取

pd.crosstab() 交叉表

日期处理的时候提取 周 ,还有 dt.isocalendar()

股票分析代码解读

unstack()

copy()

df.update() 参数 overwrite=False 空值替换

dropna() thresh 参数

df.replace() 的 regex=True参数

df.sample(frac=1)

测试用例

人员信息表 (ID、姓名、性别、年龄)

共三十行

|id|name|sex|age|
|776|亮|男|21|
|500|平|男|42|
|893|梅|女|33|
|834|昌|男|16|
|478|朋|女|58|
|494|彬|男|38|
|369|珍|女|73|
|479|童|女|15|
|836|伟|女|36|
|517|豪|男|8|
|781|然|男|16|
|470|慧|女|30|
|780|保|男|54|
|956|瑜|男|12|
|800|珍|女|57|
|505|娥|女|45|
|424|磊|男|31|
|954|香|女|53|
|637|英|女|38|
|356|芹|女|58|
|512|亮|男|15|
|887|莹|女|17|
|456|荣|女|57|
|909|平|男|65|
|743|雪|女|29|
|465|家|女|35|
|897|芹|女|54|
|796|鑫|女|8|
|677|珍|女|61|
|474|海|男|45|
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值