推荐收藏!5万字图解pandas

209 阅读28分钟

大家好,今天我用图解的方式讲解pandas的用法,内容较长建议收藏,梳理不易,点赞支持。

学习 Python 编程,给我的经验就是:技术要学会分享、交流,不建议闭门造车。一个人可能走的很快、但一堆人可以走的更远。如果你希望技术交流,可以关注我

关注公众号:Python技术训练营

解锁 《python百万字教程,手把手助你从零进阶拿高薪》,让学习更贴近未来实战

内容包含

python百万字教程,手把手助你从零进阶拿高薪(十一模块)

1.hello,初识python

2.python大数据技术体系

3.AI研发,快人一步

4.全栈开发体系,你不能错过的内容

5.掌握核心,python核心编程

6.python并发编程

7.python数据库编程

8.爬虫入门必学

9.python框架源码分析

10.爬虫必备JS逆向

11.多学一点,加薪更稳定Android逆向

整理不易,关注一下吧ღ( ´・ᴗ・` )比心🤔

第一部分:数据分析核⼼--pandas

前⾔Python作为⼀门数据可视化很好的语⾔,可以使⽤像matplotlib等库画出图形,处理数据主要使⽤pandas这⾥主要讨论Pandas

1.初识pandas

⼤多数⼈只要提及pandas,基本都知道,只要是学习python的⼈Pandas 是 Python 语⾔的⼀个扩展程序库,⽤于数据分析。Pandas 是⼀个开放源码、BSD 许可的库,提供⾼性能、易于使⽤的数据结构和数据分析⼯具。Pandas 名字衍⽣⾃术语 “panel data”(⾯板数据)和 “Python data analysis”(Python 数据分析)。Pandas ⼀个强⼤的分析结构化数据的⼯具集,基础是 Numpy(提供⾼性能的矩阵运算),其次数series,还有⼀个DataFrame,这三个⽐较常⽤。Pandas 可以从各种⽂件格式⽐如 CSV、JSON、SQL、Microsoft Excel 导⼊数据。Pandas 可以对各种数据进⾏运算操作,⽐如归并、再成形、选择,还有数据清洗和数据加⼯特征。Pandas ⼴泛应⽤在学术、⾦融、统计学等各个数据分析领域。

2.Pandas的主体

Pandas 的主要数据结构是 Series (⼀维数据)与 DataFrame(⼆维数据),这两种数据结构⾜以处理⾦融、统计、社会科学、⼯程等领域⾥的⼤多数典型⽤例。Series:带标签的⼀维同构数组,⼀种类似于⼀维数组的对象,它由⼀组数据(各种Numpy数据类型)以及⼀组与之相关的数据标签(即索引)组成。DataFrame:带标签,⼤⼩可变,⼆维异构表格。⼀个表格型的数据结构,它含有⼀组有序的列,每列可以是不同的值类型(数值、字符串、布尔型值)。DataFrame 既有⾏索引也有列索引,它可以被看做由 Series 组成的字典(共同⽤⼀个索引)。

3.Pandas的安装

⼀般情况下只要电脑上安装Python后,并将python环境布置⼊path之中,pandas安装可以在CMD中或者在Anaconda终端进⾏安装安装命令

pip install pandas

4.Pandas的应⽤

1. 导⼊pandas库

# 导⼊pandas库
import pandas as pd

2. pandas之seriesPandas Series类似表格中的⼀个列(column),类似于⼀堆数组,可以保存为任何数据类型Series由索引(index)和列组成,函数如下

pandas.Series(data, index, dtype, name, copy)

参数说明: data:⼀组数据(ndarray 类型)。 index:数据索引标签,如果不指定,默认从 0 开始。 dtype:数据类型,默认会⾃⼰判断。 name:设置名称

第二部分:Pandas 展示

如图:

它描述了一个在线商店的不同产品线,共有四种不同的产品。与前面的例子不同,它可以用NumPy数组或Pandas DataFrame表示。但让我们看一下它的一些常见操作。

1.排序

使用Pandas按列排序更具可读性,如下所示:

这里argsort(a:,1)计算使a的第二列按升序排序的排列,然后a相应地对a的行重新排序。Pandas可以一步完成。

2.按多列排序

如果我们需要使用weight列来对价格列进行排序,情况会变得更糟。这里有几个例子来说明我们的观点:

在NumPy中,我们先按重量排序,然后再按价格排序。稳定排序算法保证第一次排序的结果不会在第二次排序期间丢失。NumPy还有其他实现方法,但没有一种方法像Pandas那样简单优雅。

3. 添加一列

使用Pandas添加列在语法和架构上要好得多。下面的例子展示了如何操作:

Pandas不需要像NumPy那样为整个数组重新分配内存;它只是添加了对新列的引用,并更新了列名的registry

4. 快速元素搜索

在NumPy数组中,即使你查找的是第一个元素,你仍然需要与数组大小成正比的时间来查找它。使用Pandas,你可以索引你期望被查询最多的列,并将搜索时间减少到一个常量

index列有以下限制。

  • 它需要内存和时间来构建。
  • 它是只读的(需要在每次追加或删除操作后重新构建)。
  • 这些值不需要是唯一的,但是只有当元素是唯一的时候加速才会发生。
  • 它需要预热:第一次查询比NumPy稍慢,但后续查询明显快得多

5. 按列连接(join)

如果你想从另一张表中获取基于同一列的信息,NumPy几乎没有任何帮助。Pandas更好,特别是对于1:n的关系。 Pandas join具有所有熟悉的“内”、“左”、“右”和“全外部”连接模式。

6.按列分组

数据分析中的另一个常见操作是按列分组。例如,要获得每种产品的总销量,你可以这样做: 除了sum之外,Pandas还支持各种聚合函数:mean、max、min、count等。

7. 数据透视表

Pandas最强大的功能之一是“枢轴”表。这有点像将多维空间投影到二维平面上。 虽然用NumPy当然可以实现它,但这个功能没有开箱即用,尽管它存在于所有主要的关系数据库和电子表格应用程序(Excel,WPS)中。

Pandas用df.pivot_table将分组和旋转结合在一个工具中。

简而言之,NumPy和Pandas的两个主要区别如下: 现在,让我们看看这些功能是否以性能损失为代价

8、Pandas速度

我在Pandas的典型工作负载上对NumPy和Pandas进行了基准测试:5-100列,10³- 10⁸行,整数和浮点数。下面是1行和1亿行的结果:

看起来在每一次操作中,Pandas都比NumPy慢!

当列数增加时,情况不会改变(可以预见)。至于行数,依赖关系(在对数尺度下)如下所示: 对于小数组(少于100行),Pandas似乎比NumPy慢30倍,对于大数组(超过100万行)则慢3倍。

怎么可能呢?也许是时候提交一个功能请求,建议Pandas通过df.column.values.sum()重新实现df.column.sum()了?这里的values属性提供了访问底层NumPy数组的方法,性能提升了3 ~ 30倍。

答案是否定的。Pandas在这些基本操作方面非常缓慢,因为它正确地处理了缺失值。Pandas需要NaNs (not-a-number)来实现所有这些类似数据库的机制,比如分组和旋转,而且这在现实世界中是很常见的。在Pandas中,我们做了大量工作来统一所有支持的数据类型对NaN的使用。根据定义(在CPU级别上强制执行),nan+anything会得到nan。所以

>>> np.sum([1, np.nan, 2])  
nan

但是

>>> pd.Series([1, np.nan, 2]).sum()  
3.0

一个公平的比较是使用np.nansum代替np.sum,用np.nanmean而不是np.mean等等。突然间……

对于超过100万个元素的数组,Pandas的速度是NumPy的1.5倍。对于较小的数组,它仍然比NumPy慢15倍,但通常情况下,无论操作在0.5 ms还是0.05 ms内完成都没有太大关系——无论如何它都是快速的。

最重要的是,如果您100%确定列中没有缺失值,则使用df.column.values.sum()而不是df.column.sum()可以获得x3-x30的性能提升。在存在缺失值的情况下,Pandas的速度相当不错,甚至在巨大的数组(超过10个同质元素)方面优于NumPy。

第三部分:pandas基础

1. 什么是pandas?

pandas是处理结构化(表格数据)最常用的工具,是python数据分析和数据处理的最成熟的库。pandas最早是为了处理金融数据而开发的,开放到社区至今,已经广泛应用于各个领域中;pandas以numpy为基础,而pandas是其他应用的数据基础,如Scikit-learn等。

如果你想;

  • 快速地进行数据的统计分析
  • 像数据库一样做多数据之间的关联
  • 快速地预处理数据(删除脏数据,空数据,数据转换,缺失值处理)
  • 像Excel一样的做多维的数据透视,数据分组汇总统计
  • 绘制统计图表
  • 进行数据挖掘
  • and so on

你需要掌握pandas!

2. pandas数据结构

我们先来看看pandas的三大数据结构:Series、DataFrame和Index

2.1 Series Series可以认为是一个一维数组, 但是和数组只有位置索引不同,Series可以有不同的索引。

import pandas as pd
import numpy as np
from pandas import Series, DataFrame
series = Series([12, 21, 31], index=['a', 'b', 'c'], dtype=np.int32, name="age")

# a    12
# b    21
# c    31
# Name: age, dtype: int32

从上可知,Series组成:

  • 数据项
  • 索引index, 默认为位置索引,即0到N-1
  • 数据类型dtype,不指定pandas会自动判断
  • 数据名称name

Series有哪些属性和操作

  • 获取属性:数据项values 和索引index 名称name
# array([12, 21, 31])
series.values

# Index(['a', 'b', 'c'], dtype='object')
series.index

# age
series.name

# dtype('int32')
series.dtype
  • 按照索引获取指定数据
series[0], series['b']
# 12 21
  • 查询数据和运算
series[series > 20]
# b    21
# c    31
# Name: age, dtype: int32
s2 = series * 2
# a    24
# b    42
# c    62
# Name: age, dtype: int32

s3 = np.log1p(series)

# a    2.564949
# b    3.091042
# c    3.465736
# Name: age, dtype: float64

总结下, Series是按索引组织有序有类型的一维数组。

2.2 DataFrame DataFrame可以理解为一个或者多个Series组成的表格型的二维结构的数据。

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002, 2003],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
frame = pd.DataFrame(data)

frame.info()
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 6 entries, 0 to 5
# Data columns (total 3 columns):
# state    6 non-null object
# year     6 non-null int64
# pop      6 non-null float64
# dtypes: float64(1), int64(1), object(1)
# memory usage: 224.0+ bytes

可以通过info()函数清楚的了解DataFrame组成:索引RangeIndex、数据列Data columns和数据类型。有属性来方便获取相应的值。

# RangeIndex(start=0, stop=6, step=1)
frame.index

# Index(['state', 'year', 'pop'], dtype='object')
frame.columns

DataFrame的每一行和每一列都是Series, 可以按行和按列访问数据

frame['state']

# 0      Ohio
# 1      Ohio
# 2      Ohio
# 3    Nevada
# 4    Nevada
# 5    Nevada
# Name: state, dtype: object

frame.loc[0]
# state    Ohio
# year     2000
# pop       1.5
# Name: 0, dtype: object

frame.loc[0][1]
# 2000

DataFrame是非常重要的结构,更多的操作我们在后续陆续分享。

2.3 Index 从Series和DataFrame中都可以看到Index的身影,可以说Series和DataFrame都是带索引的数据结构。

如果你对结构化数据库如Oracle、Mysql有了解化,索引是一个重要的高效访问的方式。对于Series和DataFrame来说同样也是。

Series和DataFrame的索引和数据库索引一样,是允许重复值的。

比如设置state列为索引

frame.set_index('state')
# year    pop
# state        
# Ohio    2000    1.5
# Ohio    2001    1.7
# Ohio    2002    3.6
# Nevada    2001    2.4
# Nevada    2002    2.9
# Nevada    2003    3.2

pandas基础(2)增删改查

当我们把数据放入DataFrame后,要数据分析就需要对数据进行进行各种操作,最常见的操作就是增删改查,特别是查,查进一步就是探查分析。

1. pandas读取数据

import pandas as pd
frame = pd.read_csv('./test.csv', header=None, columns=['state', 'year', 'pop'])
frame.index = frame['state']

2. pandas查

查询pandas有很多不同的方式,pandas处理的数据为表格形式,即行列式,访问数据既可以通过行和列组合来访问。

  • 按行访问和按列访问

pandas可以按照行号的切片方式来访问,如frame[0:4] 获取前5行数据 pandas通过列名直接筛选多列的数据如frame[['pop', 'year']]获取pop和year列

  • loc和iloc 行列组合访问

loc 是按照索引名称和列名进行查询行列数据;iloc是按照行列整数来进行查询行列数据

# 访问索引为Ohio的行,year和pop列
frame.loc['Ohio', ['year', 'pop']]

# 访问year和pop列的所有行
frame.loc[:, ['year', 'pop']]

# 访问前4行
frame.iloc[0:3,]

# 访问前4行,第3列
frame.iloc[0:3, 2]
  • 条件查询

最常用的条件查询方式是通过列名直接比较,如

  • frame[frame['year'] > 2001] 表示year这一列大于2001的所有行

  • 另一种等价的写法为query: frame.query('year > 2001')

3. pandas增删改

pandas增加行和列的方式可以通过增加一个Series的方式

  • 增加一列
frame['age'] = frame['year'] - 1900
  • 增加一行
frame.loc[len(frame.index)] = ['1', 2, 4]

删除行和列

# 删除列(按照列名)
frame.drop(['age'], axis=1)

# 删除行(按照行的索引)
frame.drop(['Ohio'], axis=0)
frame.drop([6], axis=0)
# 替换第一行
frame[0:1] = [ 2, 1.2, 20]

# 修改第二行第3列
frame.iloc[1, 2] = 4 

pandas基础(3)数据对齐

索引是pandas两个重要数据结构Series和DataFrame的重要组成部分,默认的索引是以0开始序列,也可以自定义数据。

因为有索引的存在,Series和DataFrame在进行数据操作的时候,会自动进行数据对齐。

我们来看一个例子:

import pandas as pd
s1 = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
s2 = pd.Series([1, 2, 3, 4], index=['e', 'b', 'c', 'd'])

s3 = s1 + s2

# a    NaN
# b    4.0
# c    6.0
# d    8.0
# e    NaN
# dtype: float64

从上面可知,s1和s2具有不同的索引,s1 + s2 并不是Series中的数据按序相加,而是自动按照索引进行对齐,有时候我们可以利用这个特性进行数据处理,有时候需要注意这种特性会产生和预期不同的结果。

如果我们需要对s1和s2按序相加要怎么办? 这个时候需要对索引进行重置成默认序列索引,在进行相加。

s1.reset_index(drop=True) + s2.reset_index(drop=True)

# 0    2
# 1    4
# 2    6
# 3    8
# dtype: int64

上面可知,通过reset_index将索引重置到0到3, 相加就是按序相加。

reset_index默认会将索引作为新的一列增加到数据中, 如果你不需要索引可以指定drop=True

s1.reset_index()
#     index    0
# 0    a    1
# 1    b    2
# 2    c    3
# 3    d    4

pandas基础(4)-数据映射apply

我们在数据分析过程中,通常需要对原始的数据进行很多转换和处理,比如对于出生日期,我们希望获得年龄的特征;对于身高和体重的数据,我们希望或者BMI指数等等。

这种操作在分析过程是最常用的数据处理,这个过程涉及对一列或者多列数据进行操作。pandas中用apply函数来处理这个过程。

apply函数的作用:就是用某个指定的函数f来依次作用于DataFrame或者Series的每个数据,可以指定按行处理和按列处理。

我们看一个例子:

import pandas as pd
import numpy as np
data_df = pd.DataFrame({'name': ['jack', 'lili', 'abc'],
                      'birthday': ['2001-01-01', '2003-12-01', '2005-09-01'],
                      'height':[1.2, 1.5, 1.6],
                      'weight': [40, 50, 30]})

data_df['age'] = data_df['birthday'].apply(lambda x: (datetime.now().timestamp()-datetime.strptime(x, '%Y-%m-%d').timestamp())//86400/365)

data_df['age'] = data_df['age'].astype(int)

如果是计算逻辑比价简单的,处理逻辑可以通过lambda函数进行处理;比如上面的例子中通过出生日期计算年龄。默认情况下apply传入函数的参数为series(按列),映射函数对每一列的每一行数据执行函数操作。

也可以定义一个函数来代替lambda,如下cal_age代替:

def cal_age(x):
    age_ts = datetime.now().timestamp() - datetime.strptime(x, '%Y-%m-%d').timestamp()
    return age_ts // 86400 / 365

data_df['age'] = data_df['birthday'].apply(cal_age).astype(int)

如果数据处理需要多个数据,比如计算BMI指数需要升高和体重。

def bmi(x):
    return x['weight'] / x['height'] ** 2

data_df['bmi'] = data_df[['height', 'weight']].apply(bmi, axis=1)

从上可知,计算多列数据时,apply传入映射函数的参数为Series,通过axis指定按行还是按列,axis=1为按行,即每一行的多个列的值传入映射行数。以bmi例子,传入的是每个人的身高和体重参数。

第四部分:Series 和 Index

Series是NumPy中的一维数组,是表示其列的DataFrame的基本组成部分。尽管与DataFrame相比,它的实际重要性正在降低(你可以在不知道Series是什么的情况下完美地解决许多实际问题),但如果不首先学习Series和Index,你可能很难理解DataFrame是如何工作的。

在内部,Series将值存储在普通的NumPy vector中。因此,它继承了它的优点(紧凑的内存布局、快速的随机访问)和缺点(类型同质、缓慢的删除和插入)。最重要的是,Series允许使用类似于字典的结构index通过label访问它的值。标签可以是任何类型(通常是字符串和时间戳)。它们不必是唯一的,但唯一性是提高查找速度所必需的,许多操作都假定唯一性。 如你所见,现在每个元素都可以通过两种替代方式寻址:通过label(=使用索引)和通过position(=不使用索引): 按“位置”寻址有时被称为“位置索引”,这只是增加了混淆。

一对方括号是不够的。特别是:

  • S[2:3]不是解决元素2最方便的方式
  • 如果名称恰好是整数,s[1:3]就会产生歧义。它可能意味着名称1到3包含或位置索引1到3不包含。

为了解决这些问题,Pandas还有两种“风格”的方括号,你可以在下面看到: .loc总是使用标号,并且包含间隔的两端。

.iloc总是使用“位置索引”并排除右端。

使用方括号而不是圆括号的目的是为了访问Python的切片约定:你可以使用单个或双冒号,其含义是熟悉的start:stop:step。像往常一样,缺少开始(结束)意味着从序列的开始(到结束)。step参数允许使用s.iloc[::2]引用偶数行,并使用s['Paris':'Oslo':-1]以相反的顺序获取元素。

它们还支持布尔索引(使用布尔数组进行索引),如下图所示: 你可以在下图中看到它们如何支持fancy indexing(用整数数组进行索引):

Series最糟糕的地方在于它的视觉表现:出于某种原因,它没有一个很好的富文本外观,所以与DataFrame相比,它感觉像是二等公民: 我对这个Series做了补丁,让它看起来更好,如下所示: 垂直线表示这是一个Series,而不是一个DataFrame。Footer在这里被禁用了,但它可以用于显示dtype,特别是分类类型。

您还可以使用pdi.sidebyside(obj1, obj2,…)并排显示多个Series或dataframe: pdi(代表pandas illustrated)是github上的一个开源库,具有本文所需的这个和其他功能。要使用它,就要写

pip install pandas-illustrated

索引(Index)

负责通过标签获取元素的对象称为index。它非常快:无论你有5行还是50亿行,你都可以在常量时间内获取一行数据。

指数是一个真正的多态生物。默认情况下,当创建一个没有索引的序列(或DataFrame)时,它会初始化为一个惰性对象,类似于Python的range()。和range一样,几乎不使用任何内存,并且与位置索引无法区分。让我们用下面的代码创建一个包含一百万个元素的序列:

>>> s = pd.Series(np.zeros(10**6))  
>>> s.index  
RangeIndex(start=0, stop=1000000, step=1)  
>>> s.index.memory_usage()       # in bytes  
128                    # the same as for Series([0.])

现在,如果我们删除一个元素,索引隐式地转换为类似于dict的结构,如下所示:

>>> s.drop(1, inplace=True)  
>>> s.index  
Int64Index([     0,      2,      3,      4,      5,      6,      7,  
            ...  
            999993, 999994, 999995, 999996, 999997, 999998, 999999],  
           dtype='int64', length=999999)  
>>> s.index.memory_usage()  
7999992

该结构消耗8Mb内存!为了摆脱它,回到轻量级的类range结构,添加如下代码:

>>> s.reset_index(drop=True, inplace=True)  
>>> s.index  
RangeIndex(start=0, stop=999999, step=1)  
>>> s.index.memory_usage()  
128

如果你不熟悉Pandas,你可能想知道为什么Pandas自己没有做到这一点?好吧,对于非数字标签,有一点很明显:为什么(以及如何)Pandas在删除一行后,会重新标记所有后续的行?对于数值型标签,答案就有点复杂了。

首先,正如我们已经看到的,Pandas允许您纯粹按位置引用行,因此,如果您想在删除第3行之后定位第5行,则可以无需重新索引(这就是iloc的作用)。

其次,保留原始标签是一种与过去时刻保持联系的方法,就像“保存游戏”按钮一样。假设您有一个100x1000000的大表,需要查找一些数据。你正在一个接一个地进行几次查询,每次都缩小了搜索范围,但只查看了一小部分列,因为同时查看数百个字段是不切实际的。现在您已经找到感兴趣的行,您希望在原始表中查看有关它们的所有信息。数字索引可以帮助您立即获得它,而无需任何额外的努力。

一般来说,在索引中保持值的唯一性是一个好主意。例如,在索引中存在重复值时,查找速度不会得到提升。Pandas不像关系型数据库那样有“唯一约束”(该功能仍然是实验性的),但它有检查索引中的值是否唯一的函数,并以各种方式消除重复。

有时,一列不足以唯一标识一行。例如,同一个名字的城市有时会碰巧出现在不同的国家,甚至是同一个国家的不同地区。所以(城市,州)是一个比城市更好的标识一个地方的候选者。在数据库中,这被称为“复合主键”。在Pandas中,它被称为多索引(参见下面的第4部分),索引中的每一列都被称为“级别”。

索引的另一个重要特性是不可变。与DataFrame中的普通列不同,你不能就地更改它。索引中的任何更改都涉及从旧索引中获取数据,修改它,并将新数据作为新索引重新附加。通常情况下,它是透明的,这就是为什么不能直接写df.City.name = ' city ',而必须写一个不那么明显的df.rename(columns={' A ': ' A '}, inplace=True)

Index有一个名称(在MultiIndex的情况下,每个级别都有一个名称)。不幸的是,这个名称在Pandas中没有得到充分使用。一旦你在索引中包含了这一列,就不能再使用df了。不再使用列名表示法,并且必须恢复为可读性较差的df。指数还是更通用的df。loc对于多索引,情况更糟。一个明显的例外是df。Merge -你可以通过名称指定要合并的列,无论它是否在索引中。

同样的索引机制用于标记dataframe的行和列,以及序列。

按值查找元素

Series内部由一个NumPy数组和一个类似数组的结构index组成,如下所示: Index提供了一种通过标签查找值的方便方法。那么如何通过值查找标签呢?

s.index[s.tolist().find(x)]           # faster for len(s) < 1000  
s.index[np.where(s.values==x)[0][0]]  # faster for len(s) > 1000

我编写了find()和findall()两个简单的封装器,它们运行速度快(因为它们会根据序列的大小自动选择实际的命令),而且使用起来更方便。代码如下所示:

>>> import pdi  
>>> pdi.find(s, 2)  
'penguin'  
>>> pdi.findall(s, 4)  
Index(['cat', 'dog'], dtype='object')

缺失值

Pandas开发人员特别关注缺失值。通常,你通过向read_csv提供一个标志来接收一个带有NaNs的dataframe。否则,可以在构造函数或赋值运算符中使用None(尽管不同数据类型的实现略有不同,但它仍然有效)。这张图片有助于解释这个概念: 你可以使用NaNs做的第一件事是了解你是否有NaNs。从上图可以看出,isna()生成了一个布尔数组,而.sum()给出了缺失值的总数。

现在你知道了它们的存在,你可以选择用常量值填充它们或通过插值来一次性删除它们,如下所示: 另一方面,你可以继续使用它们。大多数Pandas函数会很高兴地忽略缺失值,如下图所示: 更高级的函数(median、rank、quantile等)也可以做到这一点。

算术运算与索引对齐: 如果索引中存在非唯一值,则结果不一致。不要对索引不唯一的序列使用算术运算。

比较

比较有缺失值的数组可能会比较棘手。下面是一个例子:

>>> np.all(pd.Series([1., None, 3.]) ==   
           pd.Series([1., None, 3.]))  
False  
>>> np.all(pd.Series([1, None, 3], dtype='Int64') ==   
           pd.Series([1, None, 3], dtype='Int64'))  
True  
>>> np.all(pd.Series(['a', None, 'c']) ==   
           pd.Series(['a', None, 'c']))  
False

为了正确地比较nan,需要用数组中一定没有的元素替换nan。例如,使用-1或∞:

>>> np.all(s1.fillna(np.inf) == s2.fillna(np.inf))   # works for all dtypes  
True

或者,更好的做法是使用NumPy或Pandas的标准比较函数:

>>> s = pd.Series([1., None, 3.])  
>>> np.array_equal(s.values, s.values, equal_nan=True)  
True  
>>> len(s.compare(s)) == 0  
True

这里,compare函数返回一个差异列表(实际上是一个DataFrame), array_equal则直接返回一个布尔值。

当比较混合类型的DataFrames时,NumPy比较失败(issue #19205),而Pandas工作得很好。如下所示:

>>> df = pd.DataFrame({'a': [1., None, 3.], 'b': ['x', None, 'z']})  
>>> np.array_equal(df.values, df.values, equal_nan=True)  
TypeError  
<...>  
>>> len(df.compare(df)) == 0  
True

追加、插入、删除

虽然Series对象被认为是size不可变的,但它可以在原地追加、插入和删除元素,但所有这些操作都是:

  • 慢,因为它们需要为整个对象重新分配内存和更新索引。
  • 非常不方便。

下面是插入值的一种方式和删除值的两种方式: 第二种删除值的方法(通过drop)比较慢,并且在索引中存在非唯一值时可能会导致复杂的错误。

Pandas有df.insert方法,但它只能将列(而不是行)插入到dataframe中(并且对series不起作用)。

添加和插入的另一种方法是使用iloc对DataFrame进行切片,应用必要的转换,然后使用concat将其放回。我实现了一个名为insert的函数,可以自动执行这个过程: 注意(就像在df.insert中一样)插入位置由位置0<=i<=len(s)指定,而不是索引中元素的标签。如下所示: 要按元素的名称插入,可以合并pdi。用pdi查找。插入,如下所示:

请注意,unlikedf.insert、pdi.insert返回一个副本,而不是原地修改Series/DataFrame

统计数据

Pandas提供了全方位的统计函数。它们可以让您了解百万元素序列或DataFrame中的内容,而无需手动滚动数据。

所有Pandas统计函数都会忽略NaNs,如下所示: 注意,Pandas std给出的结果与NumPy std不同,如下所示:

>>> pd.Series([1, 2]).std()  
0.7071067811865476  
>>> pd.Series([1, 2]).values.std()  
0.5

这是因为NumPy std默认使用N作为分母,而Pandas std默认使用N-1作为分母。两个std都有一个名为ddof (delta degrees of freedom)的参数,NumPy默认为0,Pandas默认为1,这可以使结果一致。N-1是你通常想要的值(在均值未知的情况下估计样本的偏差)。这里有一篇维基百科的文章详细介绍了贝塞尔的修正。

由于序列中的每个元素都可以通过标签或位置索引访问,因此argmin (argmax)有一个姐妹函数idxmin (idxmax),如下图所示: 下面是Pandas的自描述统计函数供参考:

  • std:样本标准差
  • var,无偏方差
  • sem,均值的无偏标准误差
  • quantile分位数,样本分位数(s.quantile(0.5)≈s.median())
  • oode是出现频率最高的值
  • 默认为Nlargest和nsmallest,按出现顺序排列
  • diff,第一个离散差分
  • cumsum 和 cumprod、cumulative sum和product
  • cummin和cummax,累积最小值和最大值

以及一些更专业的统计函数:

  • pct_change,当前元素与前一个元素之间的变化百分比
  • skew偏态,无偏态(三阶矩)
  • kurt或kurtosis,无偏峰度(四阶矩)
  • cov、corr和autocorr、协方差、相关和自相关
  • rolling滚动窗口、加权窗口和指数加权窗口

重复数据

在检测和处理重复数据时需要特别小心,如下图所示: drop_duplicates和duplication可以保留最后一次出现的副本,而不是第一次出现的副本。

请注意,s.a uint()比np快。唯一性(O(N) vs O(NlogN)),它会保留顺序,而不会返回排序结果。独特的。

缺失值被视为普通值,有时可能会导致令人惊讶的结果。

如果你想排除nan,需要显式地这样做。在这个例子中,是s.l opdropna().is_unique == True。

还有一类单调函数,它们的名字是自描述的:

  • s.is_monotonic_increasing ()
  • s.is_monotonic_decreasing ()
  • s._strict_monotonic_increasing ()
  • s._string_monotonic_decreasing ()
  • s.is_monotonic()。这是意料之外的,出于某种原因,这是s.is_monotonic_increasing()。它只对单调递减序列返回False。

分组

在数据处理中,一个常见的操作是计算一些统计量,不是针对整个数据集,而是针对其中的某些组。第一步是通过提供将一系列(或一个dataframe)分解为组的标准来定义一个“智能对象”。这个智能对象没有立即的表示,但可以像Series一样查询它,以获得每个组的某个属性,如下图所示: 在这个例子中,我们根据数值除以10的整数部分将序列分成三组。对于每个组,我们请求每个组中元素的和、元素的数量以及平均值。

除了这些聚合函数,您还可以根据特定元素在组中的位置或相对值访问它们。如下所示: 你也可以使用g.ag (['min', 'max'])一次调用计算多个函数,或者使用g.c describe()一次显示一堆统计函数。

如果这些还不够,你还可以通过自己的Python函数传递数据。它可以是:

一个函数f,它接受一个组x(一个Series对象)并生成一个值(例如sum())与g.eapply (f)一起使用。

一个函数f,它接受一个组x(一个Series对象),并与g.transform(f)生成一个大小与x相同的Series对象(例如cumsum())。 在上面的例子中,输入数据是有序的。groupby不需要这样做。实际上,如果分组中的元素不是连续存储的,它也同样有效,因此它更接近于collections.defaultdict,而不是itertools.groupby。它总是返回一个没有重复项的索引。 与defaultdict和关系数据库GROUP BY子句不同,Pandas groupby按组名对结果进行排序。可以用sort=False来禁用它。

免责声明:实际上,g.apply(f)比上面描述的更通用:

如果f(x)返回与x大小相同的序列,它可以模拟transform 如果f(x)返回一系列不同大小或不同的dataframe,则会得到一个具有相应多索引的序列。 但文档警告说,这些使用方法可能比相应的transform和agg方法慢,所以要小心。

第五部分:pandas数据可视化

Python数据可视化常用的是matplotlib库,matplotlib是底层库,今天学了pandas的数据可视化,相对于matplotlib库来说,简单许多。

折线图

%matplotlib inline
import numpy as np
import pandas as pd
x1 = pd.Series(np.random.normal(size=10))
x1.plot()

我们也可以加入grid参数添加格网:

x1.plot(grid=True)

条形图

依旧以前文的简书数据为例:

jianshu.groupby(jianshu.index)[['view']].sum().sort(['view'],ascending=False)[0:5].plot(kind='bar')

jianshu.groupby(jianshu.index)[['gain']].sum().sort(['gain'],ascending=False)[0:10].plot(kind='barh')

pandas绘图乱码解决

pandas绘图其实是对matplotlib库继承,而matplotlib库默认为ASCII编码,所以绘图中文会乱码。我们需要去matplotlib库里面的matplotlibrc文件进行设置。 打开matplotlibrc文件,取消如下行的注释,并在font.sans-serif冒号后加入SimHei,这个为中文字体。重启python即可。

font.family         : sans-serif
font.sans-serif     : SimHei
axes.unicode_minus 

关注公众号:Python技术训练营

解锁 《python百万字教程,手把手助你从零进阶拿高薪》,让学习更贴近未来实战

内容包含

python百万字教程,手把手助你从零进阶拿高薪(十一模块)

1.hello,初识python

2.python大数据技术体系

3.AI研发,快人一步

4.全栈开发体系,你不能错过的内容

5.掌握核心,python核心编程

6.python并发编程

7.python数据库编程

8.爬虫入门必学

9.python框架源码分析

10.爬虫必备JS逆向

11.多学一点,加薪更稳定Android逆向

整理不易,关注一下吧ღ( ´・ᴗ・` )比心🤔