3.5万字,图解 Pandas
Posted 我爱Python数据挖掘
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3.5万字,图解 Pandas相关的知识,希望对你有一定的参考价值。
大家好,在 Python 各个工具包中,最频繁使用的应该就是 Pandas 了。今天我以图解的方式给大家介绍 Pandas 中各种常用的操作,内容有点长,喜欢记得点赞、收藏、关注。
另外,为方便大家学习,PDF版已经生成,可以我找获取
第一部分:Pandas 展示
请看下表:
它描述了一个在线商店的不同产品线,共有四种不同的产品。与前面的例子不同,它可以用NumPy数组或Pandas DataFrame表示。但让我们看一下它的一些常见操作。
技术提升
技术要学会分享、交流,不建议闭门造车。一个人可以走的很快、一堆人可以走的更远。
本文来自粉丝的分享、推荐,资料干货、代码、数据、技术交流提升,均可加交流群获取,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友。
方式①、添加微信号:pythoner666,备注:来自CSDN
方式②、微信搜索公众号:Python学习与数据挖掘,后台回复:加群
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。
第二部分. 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方法慢,所以要小心。
第三部分. DataFrames
Pandas的主要数据结构是DataFrame。它将一个二维数组与它的行和列的标签捆绑在一起。它由一系列对象组成(具有共享索引),每个对象表示一列,可能具有不同的dtype。
读写CSV文件
构造DataFrame的一种常用方法是读取csv(逗号分隔值)文件,如下图所示:
pd.read_csv()函数是一个完全自动化且可疯狂定制的工具。如果你只想学习Pandas的一件事,那就学习使用read_csv——它会有回报的:)。
下面是一个解析非标准的.csv文件的例子:
以及一些简要描述:
因为CSV没有严格的规范,所以有时需要一些试错才能正确地阅读它。read_csv最酷的地方在于它会自动检测很多东西:
-
列名和类型
-
布尔值的表示
-
缺失值的表示等。
与其他自动化一样,你最好确保它做了正确的事情。如果在Jupyter单元中简单地编写df的结果碰巧太长(或太不完整),您可以尝试以下操作:
-
df.head(5)或df[:5]显示前5行
-
df.dtypes返回列的类型
-
df.shape返回行数和列数
-
Df.info()汇总所有相关信息
将一列或几列设置为索引是一个好主意。下图展示了这个过程:
Index在Pandas中有很多用途:
-
算术运算按索引对齐
-
它使按该列进行的查找更快,等等。
所有这些都是以较高的内存消耗和不太明显的语法为代价的。
构建DataFrame
另一种选择是从内存中已经存储的数据中构建一个dataframe。它的构造函数非常全能,可以转换(或包装)任何类型的数据:
在第一种情况下,在没有行标签的情况下,Pandas用连续的整数标记行。在第二种情况下,它对行和列都进行了相同的操作。为Pandas提供列的名称总是一个好主意,而不是整数标签(使用columns参数),有时也可以提供行(使用index参数,尽管rows听起来可能更直观)。这张图片会有帮助:
不幸的是,无法在DataFrame构造函数中为索引列设置名称,所以唯一的选择是手动指定,例如,df.index.name = ‘城市名称’
下一种方法是使用NumPy向量组成的字典或二维NumPy数组构造一个DataFrame:
请注意,在第二种情况下,人口数量的值被转换为浮点数。实际上,它在之前的构建NumPy数组时就发生过。这里需要注意的另一件事是,从2D NumPy数组构建dataframe默认是视图。这意味着改变原始数组中的值会改变dataframe,反之亦然。另外,它节省了内存。
第一种情况(NumPy向量组成的字典)也可以启用这种模式,设置copy=False即可。不过,它非常脆弱。简单的操作就可以把它变成副本而不需要通知。
另外两个(不太有用的)创建DataFrame的选项是:
-
从一个dict列表(其中每个dict表示一行,其键是列名,其值是相应的单元格值)
-
来自由Series组成的dict(其中每个Series表示一列;默认情况下,可以让它返回一个copy=False的视图)。
如果你“动态”注册流数据,最好的选择是使用列表的dict或列表的列表,因为Python会透明地在列表末尾预分配空间,以便快速追加。NumPy数组和Pandas dataframes都不能做到这一点。另一种可能性(如果你事先知道行数)是用DataFrame(np.zeros)之类的东西手动预分配内存。
DataFrames的基本操作
DataFrame最好的地方(在我看来)是你可以:
-
轻松访问其列,如d.area返回列值(或者df[’ Area ']——适用于包含空格的列名)
-
将列作为自变量进行操作,例如使用afterdf. population /= 10**6人口以百万计存储,下面的命令根据现有列中的值创建一个名为` density `的新列。更多信息见下图:
注意,创建新列时,即使列名中不包含空格,也必须使用方括号。
此外,你可以对不同dataframe中的列使用算术操作,只要它们的行具有有意义的标签,如下所示:
索引DataFrames
正如我们在本系列中已经看到的,普通的方括号不足以满足索引的所有需求。你不能通过名称访问行,不能通过位置索引访问不相交的行,你甚至不能引用单个单元格,因为df[‘x’, ‘y’]是为多索引保留的!
为了满足这些需求,dataframes,就像series一样,有两种可选的索引模式:按标签索引的loc和按位置索引的iloc。
在Pandas中,引用多行/多列是一个副本,而不是视图。但它是一种特殊的复制,允许赋值作为一个整体:
-
df.loc[‘a’]=10 works (一行作为一个整体是一个可写的)
-
df.loc[‘a’][‘A’]=10 works (元素访问传播到原始df)
-
df.loc[‘a’:’b’] = 10 works (assigning to a subar将整个作品赋值给一个子数组)
-
df.loc[‘a’:’b’][‘A’] = 10 doesn’t (对其元素赋值不会).
在最后一种情况下,该值只会被设置在切片的副本上,而不会反映在原始df上(会相应地显示一个警告)。
根据不同的背景,有不同的解决方案:
-
你想要改变原始的df。然后使用df。loc[’ a’: ’ b ‘, ’ a’] = 10
-
你故意创建了一个副本,然后想要处理这个副本:df1 = df.loc[’ a ': ’ b ‘];df1[’ A ‘]=10 # SettingWithCopy warning要在这种情况下消除警告,请使其成为一个真正的副本:df1 = df.loc[’ A ': ’ b '].copy();df1 [A] = 10
Pandas还支持一种方便的NumPy语法来进行布尔索引。
当使用多个条件时,必须将它们括起来,如下所示:
当你期望返回一个值时,需要特别注意。
因为可能有多行匹配条件,所以loc返回一个序列。要从中得到标量值,你可以使用:
- float(s)或更通用的s.e item(),除非序列中只有一个值,否则都会引发ValueError
S.iloc[0],仅在没有找到时引发异常;此外,它是唯一支持赋值的函数:df[…].Iloc[0] = 100,但当你想修改所有匹配时,肯定不需要它:df[…]= 100。
或者,你可以使用基于字符串的查询:
- df.query (’ name = =“Vienna”)
df.query(‘population>1e6 and area<1000’)它们更短,适合多索引,并且逻辑操作符优先于比较操作符(=需要更少的括号),但它们只能按行过滤,并且不能通过它们修改Dataframe。
几个第三方库允许你使用SQL语法直接查询dataframe (duckdb),或者通过将dataframe复制到SQLite并将结果包装回Pandas objects (pandasql)来间接查询dataframe。不出所料,直接法更快。
DataFrame算术
你可以对dataframes、series和它们的组合应用普通操作,如加、减、乘、除、求模、幂等。
所有的算术运算都是根据行标签和列标签对齐的:
在dataframe和Series之间的混合操作中,Series(天知道为什么)表现得(和广播)像一个行向量,并相应地对齐:
可能是为了与列表和一维NumPy向量保持一致(它们不按标签对齐,并被认为是一个简单的二维NumPy数组的DataFrame):
因此,在不太幸运(也是最常见的!)的情况下,将一个dataframe除以列向量序列,你必须使用方法而不是操作符,如下所示:
由于这个有问题的决定,每当你需要在dataframe和列式序列之间执行混合操作时,你必须在文档中查找它(或记住它):
结合DataFrames
Pandas有三个函数,concat、merge和join,它们做同样的事情:将来自多个dataframe的信息合并为一个。但是每个工具的实现方式都略有不同,因为它们是为不同的用例量身定制的。
垂直叠加
这可能是将两个或多个dataframe合并为一个的最简单方法:您获取第一个dataframe中的行,并将第二个dataframe中的行追加到底部。为了使其工作,这两个dataframe需要(大致)具有相同的列。这类似于NumPy中的vstack,正如你在图像中所看到的:
索引中有重复的值是不好的。你可能会遇到各种各样的问题(参见下面的` drop `示例)。即使你不关心索引,也要尽量避免出现重复的值:
-
要么使用reset_index=True参数
-
调用df.reset_index(drop=True)将行从0重新索引到len(df)-1,
-
使用keys参数可以解决MultiIndex的二义性(见下文)。
如果dataframe的列不能完美匹配(不同的顺序在这里不计算在内),Pandas可以取列的交集(默认值kind='inner ')或插入nan来标记缺失值(kind=‘outer’):
水平叠加
concat也可以执行“水平”堆叠(类似于NumPy中的hstack):
join比concat更可配置:特别是,它有五种连接模式,而concat只有两种。详情请参阅下面的“1:1关系连接”部分。
基于多指数的数据叠加
如果行标签和列标签一致,concat可以执行与垂直堆叠类似的多索引(就像NumPy中的dstack):
如果行和/或列部分重叠,Pandas将相应地对齐名称,这很可能不是你想要的。下面的图表可以帮助你将这个过程可视化:
一般来说,如果标签重叠,这意味着dataframe在某种程度上彼此相关,实体之间的关系最好使用关系数据库的术语来描述。
1:1 连接的关系
当同一组对象的信息存储在几个不同的DataFrame中时,你希望将它们合并为一个DataFrame。
如果要合并的列不在索引中,则使用merge。
它所做的第一件事是丢弃索引中的任何内容。然后执行联结操作。最后,将结果从0重新编号为n-1。
如果列已经在索引中,则可以使用join(这只是merge的别名,将left_index或right_index设置为True,并设置不同的默认值)。
从这个简化的例子中可以看出(参见上面的全外连接),与关系型数据库相比,Pandas对行顺序的处理相当轻松。左外联结和右外联结比内外联结更容易预测(至少在需要合并的列中有重复值之前是这样)。因此,如果你想保证行顺序,就必须显式地对结果进行排序。
1:n 连接的关系
这是数据库设计中使用最广泛的关系,表A中的一行(例如“State”)可以与表B中的几行(例如城市)相关联,但表B中的每一行只能与表A中的一行相关联(即一个城市只能处于一种状态,但一个状态由多个城市组成)。
就像1:1关系一样,在Pandas中连接一对1:n相关的表,你有两种选择。如果要合并的列或者不在索引中,并且可以丢弃碰巧在两张表的索引中都存在的列,则使用merge。下面的例子会有所帮助:
正如我们已经看到的,merge对行顺序的处理没有Postgres严格:所有声明的语句,保留的键顺序只适用于left_index=True和/或right_index=True(这就是join的别名),并且只在要合并的列中没有重复值的情况下。这就是为什么join有一个sort参数。
现在,如果要合并的列已经在右侧DataFrame的索引中,可以使用join(或者merge with right_index=True,这是完全相同的事情):
这次Pandas保留了左DataFrame的索引值和行顺序。
注意:注意,如果第二个表有重复的索引值,你最终将在结果中得到重复的索引值,即使左表索引是唯一的!
有时,合并的dataframe具有同名的列。merge和join都有解决二义性的方法,但语法略有不同(默认情况下merge会用` _x `, ` _y `来解决,而join会抛出异常),如下图所示:
总结:
-
合并非索引列上的连接,连接要求列被索引
-
merge丢弃左DataFrame的索引,join保留它
-
默认情况下,merge执行内联结,join执行左外联结
-
合并不保持行顺序
-
Join可以保留它们(有一些限制)
-
join是合并的别名,left_index=True和/或right_index=True
多个连接
如上所述,当对两个dataframe(如df.join(df1))运行join时,它充当了合并的别名。但是join也有一个` multiple join `模式,它只是concat(axis=1)的别名。
与普通模式相比,该模式有一些限制:
-
它没有提供解析重复列的方法
-
它只适用于1:1关系(索引到索引连接)。
因此,多个1:n关系应该一个接一个地连接。仓库` panda -illustrated `也提供了一个辅助方法,如下所示:
pdi.join是Join的一个简单包装器,它接受on、how和后缀参数,以便您可以在一个命令中进行多个联结。与原始的关联操作一样,关联的是属于第一个DataFrame的列,其他DataFrame根据它们的索引进行关联操作。
插入和删除
由于DataFrame是列的集合,因此将这些操作应用到行上比应用到列上更容易。例如,插入一列总是在原地完成,而插入一行总是会生成一个新的DataFrame,如下所示:
删除列通常不用担心,除了del df[‘D’]和del df。D则没有(Python级别的限制)。
使用drop删除行非常慢,如果原始标签不是唯一的,可能会导致复杂的bug。下图将帮助解释这个概念:
一种解决方案是使用ignore_index=True,它告诉concat在连接后重置行名称:
在这种情况下,将name列设置为索引将有所帮助。但对于更复杂的滤波器,它不会。
另一种快速、通用、甚至可以处理重复行名的解决方案是索引而不是删除。为了避免显式地否定条件,我写了一个(只有一行代码的)自动化程序。
分组
这个操作已经在Series部分详细描述过了。但是DataFrame的groupby在此基础上有一些特定的技巧。
首先,你可以使用一个名称来指定要分组的列,如下图所示:
如果没有as_index=False, Pandas将进行分组的列指定为索引。如果这不是我们想要的,可以使用reset_index()或指定as_index=False。
通常,数据框中的列比你想在结果中看到的多。默认情况下,Pandas会对所有远端可求和的东西进行求和,因此你需要缩小选择范围,如下所示:
注意,当对单个列求和时,你将得到一个Series而不是DataFrame。如果出于某种原因,你想要一个DataFrame,你可以:
-
使用双括号:df.groupby(‘product’)[[‘quantity’]].sum()
-
显式转换:df.groupby(‘product’)[‘quantity’].sum().to_frame()
切换到数值索引也会创建一个DataFrame:
-
df.groupby(‘product’, as_index=False)[‘quantity’].sum()
-
df.groupby(‘product’)[‘quantity’].sum().reset_index()
但是,尽管外观不寻常,Series的行为就像DataFrames一样,所以可能对pdi.patch_series_repr()进行“整容”就足够了。
显然,不同的列在分组时表现不同。例如,对数量求和完全没问题,但对价格求和就没有意义了。使用。agg可以为不同的列指定不同的聚合函数,如下图所示:
或者,你可以为一列创建多个聚合函数:
或者,为了避免繁琐的列重命名,你可以这样做:
有时,预定义的函数不足以产生所需的结果。例如,在平均价格时使用权重会更好。你可以为此提供一个自定义函数。与Series不同的是,该函数可以访问组中的多个列(它以子dataframe作为参数),如下所示:
不幸的是,你不能把预定义的聚合和几个列级的自定义函数结合在一起,比如上面的那个,因为agg只接受单列级的用户函数。单列范围的用户函数唯一可以访问的是索引,这在某些情况下很方便。例如,那天香蕉以5折的价格出售,如下图所示:
为了从自定义函数中访问group by列的值,它事先已经包含在索引中。
通常,定制最少的函数可以获得最好的性能。为了提高速度:
-
通过g.apply()实现多列范围的自定义函数
-
通过g.agg()实现单列范围的自定义函数(支持使用Cython或Numba进行加速)
-
预定义函数(Pandas或NumPy函数对象,或其字符串名称)。
-
预定义函数(Pandas或NumPy函数对象,或其字符串名称)。
数据透视表(pivot table)是一种有用的工具,通常与分组一起使用,从不同的角度查看数据。
旋转和`反旋转`
假设你有一个变量a,它依赖于两个参数i和j。有两种等价的方法将它表示为一个表:
当数据是“密集的”(当有很少的0元素)时,` short `格式更合适,而当数据是“稀疏的”(大多数元素为0,可以从表中省略)时,` long `格式更好。当有两个以上的参数时,情况会变得更加复杂。
当然,应该有一种简单的方法来转换这些格式。Pandas为此提供了一个简单方便的解决方案:数据透视表。
作为一个不那么抽象的例子,考虑下表中的销售数据。有两个客户购买了两种产品的指定数量最初,这个数据是短格式的。`要将其转换为`长格式`,请使用df.pivot:
该命令丢弃了与操作无关的任何信息(索引、价格),并将来自三个请求列的信息转换为长格式,将客户名称放入结果的索引中,将产品名称放入列中,将销售数量放入DataFrame的` body `中。
至于相反的操作,你可以使用stack。它将索引和列合并到MultiIndex中:
另一种选择是使用melt:
注意,melt以不同的方式对结果行进行排序。
Pivot丢失了结果的` body `的名称信息,因此无论是stack还是melt,我们都必须提醒pandas ` quantity `列的名称。
在上面的例子中,所有的值都存在,但这不是必须的:
分组值然后旋转结果的做法是如此常见,以至于groupby和pivot被捆绑在一个专用的函数(以及相应的DataFrame方法)数据透视表中:
-
如果没有columns参数,它的行为与groupby类似
-
当没有重复的行进行分组时,它的工作原理与pivot类似
-
否则,它会进行分组和旋转
aggfunc参数控制哪一个聚合函数应该用于分组行(默认为均值)。
为了方便,pivot_table可以计算小计和合计:
一旦创建,pivot表就变成了一个普通的DataFrame,因此可以使用前面描述的标准方法查询它。
当使用多索引时,透视表特别方便。我们已经见过很多Pandas函数返回多索引DataFrame的例子。让我们仔细看看。
第四部分. MultiIndex
对于从未听说过Pandas的人来说,多索引(MultiIndex)最直接的用法是使用第二个索引列作为第一个索引列的补充,以唯一地标识每行。例如,为了消除来自不同州的城市的歧义,州的名字通常附加在城市的名字后面。例如,在美国大约有40个springfield(在关系型数据库中,它被称为复合主键)。
你可以在从CSV解析DataFrame后指定要包含在索引中的列,也可以立即作为read_csv的参数。
您还可以使用append=True将现有级别添加到多重索引,如下图所示:
另一个更典型的用例是表示多维。当你有一组具有特定属性的对象或者随着时间的推移而演变的对象时。例如:
-
社会学调查的结果
-
` Titanic `数据集
-
历史天气观测
-
锦标赛排名的年表。
这也被称为“面板数据”,Pandas就是以此命名的。
让我们添加这样一个维度:
现在我们有了一个四维空间,如下所示:
-
年形成一个(几乎连续的)维度
-
城市名称沿第二条排列
-
第三个州的名字
-
特定的城市属性(“人口”、“密度”、“面积”等)在第四个维度上起到了“刻度线”的作用。
下图说明了这个概念:
为了给对应列的尺寸名称留出空间,Pandas将整个标题向上移动:
分组
关于多重索引需要注意的第一件事是,它并不按照它可能出现的情况对任何内容进行分组。在内部,它只是一个扁平的标签序列,如下所示:
你可以通过对行标签进行排序来获得相同的groupby效果:
你甚至可以通过设置相应的Pandas选项来完全禁用视觉分组
:pd.options.display.multi_sparse=False。
类型转换
Pandas(以及Python本身)区分数字和字符串,因此在无法自动检测数据类型时,通常最好将数字转换为字符串:
pdi.set_level(df.columns, 0, pdi.get_level(df.columns, 0).astype('int'))
如果你喜欢冒险,可以使用标准工具做同样的事情:
df.columns = df.columns.set_levels(df.columns.levels[0].astype(int), level=0)
但为了正确使用它们,你需要理解什么是` levels `和` codes `,而pdi允许你使用多索引,就像使用普通的列表或NumPy数组一样。
如果你真的想知道,` levels `和` codes `是特定级别的常规标签列表被分解成的东西,以加速像pivot、join等操作:
-
pdi.get_level(df, 0) == Int64Index([2010, 2010, 2020, 2020])
-
df.columns.levels[0] == Int64Index([2010, 2020])
-
df.columns.codes[0] == Int64Index([0, 1, 0, 1])
使用多重索引构建一个Dataframe
除了从CSV文件读取和从现有列构建外,还有一些方法可以创建多重索引。它们不太常用——主要用于测试和调试。
由于历史原因,使用Panda自己的多索引表示的最直观的方法不起作用。
这里的` Levels `和` codes `(现在)被认为是不应该暴露给最终用户的实现细节,但我们已经拥有了我们所拥有的。
可能最简单的构建多重索引的方法如下:
这样做的缺点是必须在单独的一行中指定级别的名称。有几种可选的构造函数将名称和标签捆绑在一起。
当关卡形成规则结构时,您可以指定关键元素,并让Pandas自动交织它们,如下所示:
上面列出的所有方法也适用于列。例如:
使用多重索引进行索引
通过多重索引访问DataFrame的好处是,您可以轻松地使用熟悉的语法一次引用所有级别(可能省略内部级别)。
列——通过普通的方括号
行和单元格——使用.loc[]
现在,如果你想选择俄勒冈州的所有城市,或者只留下包含人口的列,该怎么办?Python语法在这里有两个限制。
1. 没有办法区分df[‘a’, ‘b’]和df[(‘a’, ‘b’)]——它是以同样的方式处理的,所以你不能只写df[:, ’ Oregon ']。否则,Pandas将永远不知道你指的是列Oregon还是第二级行Oregon
2. Python只允许在方括号内使用冒号,而不允许在圆括号内使用冒号,所以你不能写df.loc[(:, ‘Oregon’),:]
在技术方面,这并不难安排。我给DataFrame打了猴补丁,添加了这样的功能,你可以在这里看到:
这种语法唯一的缺点是,当你使用两个索引器时,它返回一个副本,所以你不能写df.mi[:, ’ Oregon ‘]。Co [’ population '] = 10。有许多可选的索引器,其中一些允许这样的赋值,但它们都有自己的特点:
1. 您可以将内层与外层交换,并使用括号。
因此,df[:, ‘population’]可以用df.swaplevel(axis=1)[‘population’]实现。
这感觉很hacky,不方便超过两层。
2. 你可以使用xs方法:df.xs (’ population ', level=1, axis=1)。
它给人的感觉不够python化,尤其是在选择多个关卡时。这种方法无法同时过滤行和列,因此名称xs(代表“横截面”)背后的原因并不完全清楚。它不能用于设置值。
3.可以为pd创建别名。idx=pd.IndexSlice;df.loc [:, idx[:, ’ population ']]
这更符合python风格,但要访问元素,必须使用别名,这有点麻烦(没有别名的代码太长了)。您可以同时选择行和列。可写的。
4. 你可以学习如何使用slice代替冒号。如果你知道a[3:10:2] == a[slice(3,10,2)],那么你可能也会理解下面的代码:df.loc[:, (slice(None), ’ population ')],但它几乎无法读懂。您可以同时选择行和列。可写的。
作为底线,Pandas有多种使用括号使用多重索引访问DataFrame元素的方法,但没有一种方法足够方便,因此他们不得不采用另一种索引语法:
5. 一个用于.query方法的迷你语言:df.query(’ state==“Oregon” or city==“Portland” ')。
它方便快捷,但缺乏IDE的支持(没有自动补全,没有语法高亮等),而且它只过滤行,而不是列。这意味着你不能在不转置DataFrame的情况下用它实现df[:, ’ population '](除非所有列的类型都相同,否则会丢失类型)。Non-writable。
一、数据结构介绍
在pandas中有两类非常重要的数据结构,即序列Series和数据框DataFrame。Series类似于numpy中的一维数组,除了通吃一维数组可用的函数或方法,而且其可通过索引标签的方式获取数据,还具有索引的自动对齐功能;DataFrame类似于numpy中的二维数组,同样可以通用numpy数组的函数和方法,而且还具有其他灵活应用,后续会介绍到。
1、Series的创建
序列的创建主要有三种方式:
1)通过一维数组创建序列
import numpy as np, pandas as pd
arr1 = np.arange(10)
arr1
type(arr1)
s1 = pd.Series(arr1)
s1
type(s1)
2)通过字典的方式创建序列
dic1 = {‘a‘:10,‘b‘:20,‘c‘:30,‘d‘:40,‘e‘:50}
dic1
type(dic1)
s2 = pd.Series(dic1)
s2
type(s2)
3)通过DataFrame中的某一行或某一列创建序列
这部分内容我们放在后面讲,因为下面就开始将DataFrame的创建。
2、DataFrame的创建
数据框的创建主要有三种方式:
1)通过二维数组创建数据框
arr2 = np.array(np.arange(12)).reshape(4,3)
arr2
type(arr2)
df1 = pd.DataFrame(arr2)
df1
type(df1)
2)通过字典的方式创建数据框
以下以两种字典来创建数据框,一个是字典列表,一个是嵌套字典。
dic2 = {‘a‘:[1,2,3,4],‘b‘:[5,6,7,8],
‘c‘:[9,10,11,12],‘d‘:[13,14,15,16]}
dic2
type(dic2)
df2 = pd.DataFrame(dic2)
df2
type(df2)
dic3 = {‘one‘:{‘a‘:1,‘b‘:2,‘c‘:3,‘d‘:4},
‘two‘:{‘a‘:5,‘b‘:6,‘c‘:7,‘d‘:8},
‘three‘:{‘a‘:9,‘b‘:10,‘c‘:11,‘d‘:12}}
dic3
type(dic3)
df3 = pd.DataFrame(dic3)
df3
type(df3)
3)通过数据框的方式创建数据框
df4 = df3[[‘one‘,‘three‘]]
df4
type(df4)
s3 = df3[‘one‘]
s3
type(s3)
二、数据索引index
细致的朋友可能会发现一个现象,不论是序列也好,还是数据框也好,对象的最左边总有一个非原始数据对象,这个是什么呢?不错,就是我们接下来要介绍的索引。
在我看来,序列或数据框的索引有两大用处,一个是通过索引值或索引标签获取目标数据,另一个是通过索引,可以使序列或数据框的计算、操作实现自动化对齐,下面我们就来看看这两个功能的应用。
1、通过索引值或索引标签获取数据
s4 = pd.Series(np.array([1,1,2,3,5,8]))
s4
如果不给序列一个指定的索引值,则序列自动生成一个从0开始的自增索引。可以通过index查看序列的索引:
s4.index
现在我们为序列设定一个自定义的索引值:
s4.index = [‘a‘,‘b‘,‘c‘,‘d‘,‘e‘,‘f‘]
s4
序列有了索引,就可以通过索引值或索引标签进行数据的获取:
s4[3]
s4[‘e‘]
s4[[1,3,5]]
s4[[‘a‘,‘b‘,‘d‘,‘f‘]]
s4[:4]
s4[‘c‘:]
s4[‘b‘:‘e‘]
千万注意:如果通过索引标签获取数据的话,末端标签所对应的值是可以返回的!在一维数组中,就无法通过索引标签获取数据,这也是序列不同于一维数组的一个方面。
2、自动化对齐
如果有两个序列,需要对这两个序列进行算术运算,这时索引的存在就体现的它的价值了—自动化对齐.
s5 = pd.Series(np.array([10,15,20,30,55,80]),
index = [‘a‘,‘b‘,‘c‘,‘d‘,‘e‘,‘f‘])
s5
s6 = pd.Series(np.array([12,11,13,15,14,16]),
index = [‘a‘,‘c‘,‘g‘,‘b‘,‘d‘,‘f‘])
s6
s5 + s6
s5/s6
由于s5中没有对应的g索引,s6中没有对应的e索引,所以数据的运算会产生两个缺失值NaN。注意,这里的算术结果就实现了两个序列索引的自动对齐,而非简单的将两个序列加总或相除。对于数据框的对齐,不仅仅是行索引的自动对齐,同时也会自动对齐列索引(变量名)
数据框中同样有索引,而且数据框是二维数组的推广,所以其不仅有行索引,而且还存在列索引,关于数据框中的索引相比于序列的应用要强大的多,这部分内容将放在数据查询中讲解。
三、利用pandas查询数据
这里的查询数据相当于R语言里的subset功能,可以通过布尔索引有针对的选取原数据的子集、指定行、指定列等。我们先导入一个student数据集:
student = pd.io.parsers.read_csv(‘C:\Users\admin\Desktop\student.csv‘)
查询数据的前5行或末尾5行
student.head()
student.tail()
查询指定的行
student.ix[[0,2,4,5,7]] #这里的ix索引标签函数必须是中括号[]
查询指定的列
student[[‘Name‘,‘Height‘,‘Weight‘]].head() #如果多个列的话,必须使用双重中括号
也可以通过ix索引标签查询指定的列
student.ix[:,[‘Name‘,‘Height‘,‘Weight‘]].head()
查询指定的行和列
student.ix[[0,2,4,5,7],[‘Name‘,‘Height‘,‘Weight‘]].head()
以上是从行或列的角度查询数据的子集,现在我们来看看如何通过布尔索引实现数据的子集查询。
查询所有女生的信息
student[student[‘Sex‘]==‘F‘]
查询出所有12岁以上的女生信息
student[(student[‘Sex‘]==‘F‘) & (student[‘Age‘]>12)]
查询出所有12岁以上的女生姓名、身高和体重
student[(student[‘Sex‘]==‘F‘) & (student[‘Age‘]>12)][[‘Name‘,‘Height‘,‘Weight‘]]
上面的查询逻辑其实非常的简单,需要注意的是,如果是多个条件的查询,必须在&(且)或者|(或)的两端条件用括号括起来。
四、统计分析
pandas模块为我们提供了非常多的描述性统计分析的指标函数,如总和、均值、最小值、最大值等,我们来具体看看这些函数:
首先随机生成三组数据
np.random.seed(1234)
d1 = pd.Series(2*np.random.normal(size = 100)+3)
d2 = np.random.f(2,4,size = 100)
d3 = np.random.randint(1,100,size = 100)
d1.count() #非空元素计算
d1.min() #最小值
d1.max() #最大值
d1.idxmin() #最小值的位置,类似于R中的which.min函数
d1.idxmax() #最大值的位置,类似于R中的which.max函数
d1.quantile(0.1) #10%分位数
d1.sum() #求和
d1.mean() #均值
d1.median() #中位数
d1.mode() #众数
d1.var() #方差
d1.std() #标准差
d1.mad() #平均绝对偏差
d1.skew() #偏度
d1.kurt() #峰度
d1.describe() #一次性输出多个描述性统计指标
必须注意的是,descirbe方法只能针对序列或数据框,一维数组是没有这个方法的
这里自定义一个函数,将这些统计描述指标全部汇总到一起:
def stats(x):
return pd.Series([x.count(),x.min(),x.idxmin(),
x.quantile(.25),x.median(),
x.quantile(.75),x.mean(),
x.max(),x.idxmax(),
x.mad(),x.var(),
x.std(),x.skew(),x.kurt()],
index = [‘Count‘,‘Min‘,‘Whicn_Min‘,
‘Q1‘,‘Median‘,‘Q3‘,‘Mean‘,
‘Max‘,‘Which_Max‘,‘Mad‘,
‘Var‘,‘Std‘,‘Skew‘,‘Kurt‘])
stats(d1)
在实际的工作中,我们可能需要处理的是一系列的数值型数据框,如何将这个函数应用到数据框中的每一列呢?可以使用apply函数,这个非常类似于R中的apply的应用方法。
将之前创建的d1,d2,d3数据构建数据框:
df = pd.DataFrame(np.array([d1,d2,d3]).T,columns=[‘x1‘,‘x2‘,‘x3‘])
df.head()
df.apply(stats)
非常完美,就这样很简单的创建了数值型数据的统计性描述。如果是离散型数据呢?就不能用这个统计口径了,我们需要统计离散变量的观测数、唯一值个数、众数水平及个数。你只需要使用describe方法就可以实现这样的统计了。
student[‘Sex‘].describe()
除以上的简单描述性统计之外,还提供了连续变量的相关系数(corr)和协方差矩阵(cov)的求解,这个跟R语言是一致的用法。
df.corr()
关于相关系数的计算可以调用pearson方法或kendell方法或spearman方法,默认使用pearson方法。
df.corr(‘spearman‘)
如果只想关注某一个变量与其余变量的相关系数的话,可以使用corrwith,如下方只关心x1与其余变量的相关系数:
df.corrwith(df[‘x1‘])
数值型变量间的协方差矩阵
df.cov()
五、类似于SQL的操作
在SQL中常见的操作主要是增、删、改、查几个动作,那么pandas能否实现对数据的这几项操作呢?答案是Of Course!
增:添加新行或增加新列
In [99]: dic = {‘Name‘:[‘LiuShunxiang‘,‘Zhangshan‘],
...: ‘Sex‘:[‘M‘,‘F‘],‘Age‘:[27,23],
...: ‘Height‘:[165.7,167.2],‘Weight‘:[61,63]}
In [100]: student2 = pd.DataFrame(dic)
In [101]: student2
Out[101]:
Age Height Name Sex Weight
0 27 165.7 LiuShunxiang M 61
1 23 167.2 Zhangshan F 63
现在将student2中的数据新增到student中,可以通过concat函数实现:
注意到了吗?在数据库中union必须要求两张表的列顺序一致,而这里concat函数可以自动对齐两个数据框的变量!
新增列的话,其实在pandas中就更简单了,例如在student2中新增一列学生成绩:
对于新增的列没有赋值,就会出现空NaN的形式。
删:删除表、观测行或变量列
删除数据框student2,通过del命令实现,该命令可以删除Python的所有对象。
删除指定的行
原数据中的第1,2,4,7行的数据已经被删除了。
根据布尔索引删除行数据,其实这个删除就是保留删除条件的反面数据,例如删除所有14岁以下的学生:
删除指定的列
我们发现,不论是删除行还是删除列,都可以通过drop方法实现,只需要设定好删除的轴即可,即调整drop方法中的axis参数。默认该参数为0,表示删除行观测,如果需要删除列变量,则需设置为1。
改:修改原始记录的值
如果发现表中的某些数据错误了,如何更改原来的值呢?我们试试结合布尔索引和赋值的方法:
例如发现student3中姓名为Liushunxiang的学生身高错了,应该是173,如何改呢?
这样就可以把原来的身高修改为现在的170了。
看,关于索引的操作非常灵活、方便吧,就这样轻松搞定数据的更改。
查:有关数据查询部分,上面已经介绍过,下面重点讲讲聚合、排序和多表连接操作。
聚合:pandas模块中可以通过groupby()函数实现数据的聚合操作
根据性别分组,计算各组别中学生身高和体重的平均值:
如果不对原始数据作限制的话,聚合函数会自动选择数值型数据进行聚合计算。如果不想对年龄计算平均值的话,就需要剔除改变量:
groupby还可以使用多个分组变量,例如根本年龄和性别分组,计算身高与体重的平均值:
当然,还可以对每个分组计算多个统计量:
是不是很简单,只需一句就能完成SQL中的SELECT…FROM…GROUP BY…功能,何乐而不为呢?
排序:
排序在日常的统计分析中还是比较常见的操作,我们可以使用order、sort_index和sort_values实现序列和数据框的排序工作:
我们再试试降序排序的设置:
上面两个结果其实都是按值排序,并且结果中都给出了警告信息,即建议使用sort_values()函数进行按值排序。
在数据框中一般都是按值排序,例如:
多表连接:
多表之间的连接也是非常常见的数据库操作,连接分内连接和外连接,在数据库语言中通过join关键字实现,pandas我比较建议使用merger函数实现数据的各种连接操作。
如下是构造一张学生的成绩表:
现在想把学生表student与学生成绩表score做一个关联,该如何操作呢?
注意,默认情况下,merge函数实现的是两个表之间的内连接,即返回两张表中共同部分的数据。可以通过how参数设置连接的方式,left为左连接;right为右连接;outer为外连接。
左连接实现的是保留student表中的所有信息,同时将score表的信息与之配对,能配多少配多少,对于没有配对上的Name,将会显示成绩为NaN。
-
六、缺失值处理
现实生活中的数据是非常杂乱的,其中缺失值也是非常常见的,对于缺失值的存在可能会影响到后期的数据分析或挖掘工作,那么我们该如何处理这些缺失值呢?常用的有三大类方法,即删除法、填补法和插值法。
删除法:当数据中的某个变量大部分值都是缺失值,可以考虑删除改变量;当缺失值是随机分布的,且缺失的数量并不是很多是,也可以删除这些缺失的观测。
替补法:对于连续型变量,如果变量的分布近似或就是正态分布的话,可以用均值替代那些缺失值;如果变量是有偏的,可以使用中位数来代替那些缺失值;对于离散型变量,我们一般用众数去替换那些存在缺失的观测。
插补法:插补法是基于蒙特卡洛模拟法,结合线性模型、广义线性模型、决策树等方法计算出来的预测值替换缺失值。
我们这里就介绍简单的删除法和替补法:
这是一组含有缺失值的序列,我们可以结合sum函数和isnull函数来检测数据中含有多少缺失值:
In [130]: sum(pd.isnull(s))
Out[130]: 9
直接删除缺失值
默认情况下,dropna会删除任何含有缺失值的行,我们再构造一个数据框试试:
返回结果表明,数据中只要含有缺失值NaN,该数据行就会被删除,如果使用参数how=’all’,则表明只删除所有行为缺失值的观测。
使用一个常量来填补缺失值,可以使用fillna函数实现简单的填补工作:
1)用0填补所有缺失值
2)采用前项填充或后向填充
3)使用常量填充不同的列
4)用均值或中位数填充各自的列
很显然,在使用填充法时,相对于常数填充或前项、后项填充,使用各列的众数、均值或中位数填充要更加合理一点,这也是工作中常用的一个快捷手段。
七、数据透视表
在Excel中有一个非常强大的功能就是数据透视表,通过托拉拽的方式可以迅速的查看数据的聚合情况,这里的聚合可以是计数、求和、均值、标准差等。
pandas为我们提供了非常强大的函数pivot_table(),该函数就是实现数据透视表功能的。对于上面所说的一些聚合函数,可以通过参数aggfunc设定。我们先看看这个函数的语法和参数吧:
pivot_table(data,values=None,
index=None,
columns=None,
aggfunc=‘mean‘,
fill_value=None,
margins=False,
dropna=True,
margins_name=‘All‘)
data:需要进行数据透视表操作的数据框
values:指定需要聚合的字段
index:指定某些原始变量作为行索引
columns:指定哪些离散的分组变量
aggfunc:指定相应的聚合函数
fill_value:使用一个常数替代缺失值,默认不替换
margins:是否进行行或列的汇总,默认不汇总
dropna:默认所有观测为缺失的列
margins_name:默认行汇总或列汇总的名称为‘All‘
我们仍然以student表为例,来认识一下数据透视表pivot_table函数的用法:
对一个分组变量(Sex),一个数值变量(Height)作统计汇总
对一个分组变量(Sex),两个数值变量(Height,Weight)作统计汇总
对两个分组变量(Sex,Age),两个数值变量(Height,Weight)作统计汇总
很显然这样的结果并不像Excel中预期的那样,该如何变成列联表的形式的?很简单,只需将结果进行非堆叠操作(unstack)即可:
看,这样的结果是不是比上面那种看起来更舒服一点?
使用多个聚合函数
有关更多数据透视表的操作,可参考《Pandas透视表(pivot_table)详解》一文,链接地址:http://python.jobbole.com/81212/
八、多层索引的使用
最后我们再来讲讲pandas中的一个重要功能,那就是多层索引。在序列中它可以实现在一个轴上拥有多个索引,就类似于Excel中常见的这种形式:
对于这样的数据格式有什么好处呢?pandas可以帮我们实现用低维度形式处理高维数数据,这里举个例子也许你就能明白了:
对于这种多层次索引的序列,取数据就显得非常简单了:
对于这种多层次索引的序列,我们还可以非常方便的将其转换为数据框的形式:
以上针对的是序列的多层次索引,数据框也同样有多层次的索引,而且每条轴上都可以有这样的索引,就类似于Excel中常见的这种形式:
我们不妨构造一个类似的高维数据框:
同样,数据框中的多层索引也可以非常便捷的取出大块数据:
在数据框中使用多层索引,可以将整个数据集控制在二维表结构中,这对于数据重塑和基于分组的操作(如数据透视表的生成)比较有帮助。
就拿student二维数据框为例,我们构造一个多层索引数据集:
讲到这里,我们关于pandas模块的学习基本完成,其实在掌握了pandas这8个主要的应用方法就可以灵活的解决很多工作中的数据处理、统计分析等任务。有关更多的pandas介绍,可参考pandas官方文档:http://pandas.pydata.org/pandas-docs/version/0.17.0/whatsnew.html。 ####感谢刘顺祥作者分享……Y(^_^)Y####
以上是关于3.5万字,图解 Pandas的主要内容,如果未能解决你的问题,请参考以下文章
1.5 万字 + 40 张图解 HTTP 常见面试题(值得收藏)
2 万字 + 20张图| 细说 Redis 九种数据类型和应用场景
import numpy as np, pandas as pd
arr1 = np.arange(10)
arr1
type(arr1)
s1 = pd.Series(arr1)
s1
type(s1)
dic1 = {‘a‘:10,‘b‘:20,‘c‘:30,‘d‘:40,‘e‘:50}
dic1
type(dic1)
s2 = pd.Series(dic1)
s2
type(s2)
arr2 = np.array(np.arange(12)).reshape(4,3)
arr2
type(arr2)
df1 = pd.DataFrame(arr2)
df1
type(df1)
dic2 = {‘a‘:[1,2,3,4],‘b‘:[5,6,7,8],
‘c‘:[9,10,11,12],‘d‘:[13,14,15,16]}
dic2
type(dic2)
df2 = pd.DataFrame(dic2)
df2
type(df2)
dic3 = {‘one‘:{‘a‘:1,‘b‘:2,‘c‘:3,‘d‘:4},
‘two‘:{‘a‘:5,‘b‘:6,‘c‘:7,‘d‘:8},
‘three‘:{‘a‘:9,‘b‘:10,‘c‘:11,‘d‘:12}}
dic3
type(dic3)
df3 = pd.DataFrame(dic3)
df3
type(df3)
df4 = df3[[‘one‘,‘three‘]]
df4
type(df4)
s3 = df3[‘one‘]
s3
type(s3)
在我看来,序列或数据框的索引有两大用处,一个是通过索引值或索引标签获取目标数据,另一个是通过索引,可以使序列或数据框的计算、操作实现自动化对齐,下面我们就来看看这两个功能的应用。
s4 = pd.Series(np.array([1,1,2,3,5,8]))
s4
s4.index
s4.index = [‘a‘,‘b‘,‘c‘,‘d‘,‘e‘,‘f‘]
s4
s4[3]
s4[‘e‘]
s4[[1,3,5]]
s4[[‘a‘,‘b‘,‘d‘,‘f‘]]
s4[:4]
s4[‘c‘:]
s4[‘b‘:‘e‘]
s5 = pd.Series(np.array([10,15,20,30,55,80]),
index = [‘a‘,‘b‘,‘c‘,‘d‘,‘e‘,‘f‘])
s5
s6 = pd.Series(np.array([12,11,13,15,14,16]),
index = [‘a‘,‘c‘,‘g‘,‘b‘,‘d‘,‘f‘])
s6
s5 + s6
s5/s6
student = pd.io.parsers.read_csv(‘C:\Users\admin\Desktop\student.csv‘)
student.head()
student.tail()
student.ix[[0,2,4,5,7]] #这里的ix索引标签函数必须是中括号[]
student[[‘Name‘,‘Height‘,‘Weight‘]].head() #如果多个列的话,必须使用双重中括号
student.ix[:,[‘Name‘,‘Height‘,‘Weight‘]].head()
student.ix[[0,2,4,5,7],[‘Name‘,‘Height‘,‘Weight‘]].head()
查询所有女生的信息
student[student[‘Sex‘]==‘F‘]
student[(student[‘Sex‘]==‘F‘) & (student[‘Age‘]>12)]
student[(student[‘Sex‘]==‘F‘) & (student[‘Age‘]>12)][[‘Name‘,‘Height‘,‘Weight‘]]
首先随机生成三组数据
np.random.seed(1234)
d1 = pd.Series(2*np.random.normal(size = 100)+3)
d2 = np.random.f(2,4,size = 100)
d3 = np.random.randint(1,100,size = 100)
d1.count() #非空元素计算
d1.min() #最小值
d1.max() #最大值
d1.idxmin() #最小值的位置,类似于R中的which.min函数
d1.idxmax() #最大值的位置,类似于R中的which.max函数
d1.quantile(0.1) #10%分位数
d1.sum() #求和
d1.mean() #均值
d1.median() #中位数
d1.mode() #众数
d1.var() #方差
d1.std() #标准差
d1.mad() #平均绝对偏差
d1.skew() #偏度
d1.kurt() #峰度
d1.describe() #一次性输出多个描述性统计指标
def stats(x):
return pd.Series([x.count(),x.min(),x.idxmin(),
x.quantile(.25),x.median(),
x.quantile(.75),x.mean(),
x.max(),x.idxmax(),
x.mad(),x.var(),
x.std(),x.skew(),x.kurt()],
index = [‘Count‘,‘Min‘,‘Whicn_Min‘,
‘Q1‘,‘Median‘,‘Q3‘,‘Mean‘,
‘Max‘,‘Which_Max‘,‘Mad‘,
‘Var‘,‘Std‘,‘Skew‘,‘Kurt‘])
stats(d1)
将之前创建的d1,d2,d3数据构建数据框:
df = pd.DataFrame(np.array([d1,d2,d3]).T,columns=[‘x1‘,‘x2‘,‘x3‘])
df.head()
df.apply(stats)
student[‘Sex‘].describe()
df.corr()
df.corr(‘spearman‘)
df.corrwith(df[‘x1‘])
df.cov()
五、类似于SQL的操作
在SQL中常见的操作主要是增、删、改、查几个动作,那么pandas能否实现对数据的这几项操作呢?答案是Of Course!
增:添加新行或增加新列
In [99]: dic = {‘Name‘:[‘LiuShunxiang‘,‘Zhangshan‘],
...: ‘Sex‘:[‘M‘,‘F‘],‘Age‘:[27,23],
...: ‘Height‘:[165.7,167.2],‘Weight‘:[61,63]}
In [100]: student2 = pd.DataFrame(dic)
In [101]: student2
Out[101]:
Age Height Name Sex Weight
0 27 165.7 LiuShunxiang M 61
1 23 167.2 Zhangshan F 63
现在将student2中的数据新增到student中,可以通过concat函数实现:
注意到了吗?在数据库中union必须要求两张表的列顺序一致,而这里concat函数可以自动对齐两个数据框的变量!
新增列的话,其实在pandas中就更简单了,例如在student2中新增一列学生成绩:
对于新增的列没有赋值,就会出现空NaN的形式。
删:删除表、观测行或变量列
删除数据框student2,通过del命令实现,该命令可以删除Python的所有对象。
删除指定的行
原数据中的第1,2,4,7行的数据已经被删除了。
根据布尔索引删除行数据,其实这个删除就是保留删除条件的反面数据,例如删除所有14岁以下的学生:
删除指定的列
我们发现,不论是删除行还是删除列,都可以通过drop方法实现,只需要设定好删除的轴即可,即调整drop方法中的axis参数。默认该参数为0,表示删除行观测,如果需要删除列变量,则需设置为1。
改:修改原始记录的值
如果发现表中的某些数据错误了,如何更改原来的值呢?我们试试结合布尔索引和赋值的方法:
例如发现student3中姓名为Liushunxiang的学生身高错了,应该是173,如何改呢?
这样就可以把原来的身高修改为现在的170了。
看,关于索引的操作非常灵活、方便吧,就这样轻松搞定数据的更改。
查:有关数据查询部分,上面已经介绍过,下面重点讲讲聚合、排序和多表连接操作。
聚合:pandas模块中可以通过groupby()函数实现数据的聚合操作
根据性别分组,计算各组别中学生身高和体重的平均值:
如果不对原始数据作限制的话,聚合函数会自动选择数值型数据进行聚合计算。如果不想对年龄计算平均值的话,就需要剔除改变量:
groupby还可以使用多个分组变量,例如根本年龄和性别分组,计算身高与体重的平均值:
当然,还可以对每个分组计算多个统计量:
是不是很简单,只需一句就能完成SQL中的SELECT…FROM…GROUP BY…功能,何乐而不为呢?
排序:
排序在日常的统计分析中还是比较常见的操作,我们可以使用order、sort_index和sort_values实现序列和数据框的排序工作:
我们再试试降序排序的设置:
上面两个结果其实都是按值排序,并且结果中都给出了警告信息,即建议使用sort_values()函数进行按值排序。
在数据框中一般都是按值排序,例如:
多表连接:
多表之间的连接也是非常常见的数据库操作,连接分内连接和外连接,在数据库语言中通过join关键字实现,pandas我比较建议使用merger函数实现数据的各种连接操作。
如下是构造一张学生的成绩表:
现在想把学生表student与学生成绩表score做一个关联,该如何操作呢?
注意,默认情况下,merge函数实现的是两个表之间的内连接,即返回两张表中共同部分的数据。可以通过how参数设置连接的方式,left为左连接;right为右连接;outer为外连接。
左连接实现的是保留student表中的所有信息,同时将score表的信息与之配对,能配多少配多少,对于没有配对上的Name,将会显示成绩为NaN。
六、缺失值处理
现实生活中的数据是非常杂乱的,其中缺失值也是非常常见的,对于缺失值的存在可能会影响到后期的数据分析或挖掘工作,那么我们该如何处理这些缺失值呢?常用的有三大类方法,即删除法、填补法和插值法。
删除法:当数据中的某个变量大部分值都是缺失值,可以考虑删除改变量;当缺失值是随机分布的,且缺失的数量并不是很多是,也可以删除这些缺失的观测。
替补法:对于连续型变量,如果变量的分布近似或就是正态分布的话,可以用均值替代那些缺失值;如果变量是有偏的,可以使用中位数来代替那些缺失值;对于离散型变量,我们一般用众数去替换那些存在缺失的观测。
插补法:插补法是基于蒙特卡洛模拟法,结合线性模型、广义线性模型、决策树等方法计算出来的预测值替换缺失值。
我们这里就介绍简单的删除法和替补法:
这是一组含有缺失值的序列,我们可以结合sum函数和isnull函数来检测数据中含有多少缺失值:
In [130]: sum(pd.isnull(s))
Out[130]: 9
直接删除缺失值
默认情况下,dropna会删除任何含有缺失值的行,我们再构造一个数据框试试:
返回结果表明,数据中只要含有缺失值NaN,该数据行就会被删除,如果使用参数how=’all’,则表明只删除所有行为缺失值的观测。
使用一个常量来填补缺失值,可以使用fillna函数实现简单的填补工作:
1)用0填补所有缺失值
2)采用前项填充或后向填充
3)使用常量填充不同的列
4)用均值或中位数填充各自的列
很显然,在使用填充法时,相对于常数填充或前项、后项填充,使用各列的众数、均值或中位数填充要更加合理一点,这也是工作中常用的一个快捷手段。
七、数据透视表
在Excel中有一个非常强大的功能就是数据透视表,通过托拉拽的方式可以迅速的查看数据的聚合情况,这里的聚合可以是计数、求和、均值、标准差等。
pandas为我们提供了非常强大的函数pivot_table(),该函数就是实现数据透视表功能的。对于上面所说的一些聚合函数,可以通过参数aggfunc设定。我们先看看这个函数的语法和参数吧:
pivot_table(data,values=None,
index=None,
columns=None,
aggfunc=‘mean‘,
fill_value=None,
margins=False,
dropna=True,
margins_name=‘All‘)
data:需要进行数据透视表操作的数据框
values:指定需要聚合的字段
index:指定某些原始变量作为行索引
columns:指定哪些离散的分组变量
aggfunc:指定相应的聚合函数
fill_value:使用一个常数替代缺失值,默认不替换
margins:是否进行行或列的汇总,默认不汇总
dropna:默认所有观测为缺失的列
margins_name:默认行汇总或列汇总的名称为‘All‘
我们仍然以student表为例,来认识一下数据透视表pivot_table函数的用法:
对一个分组变量(Sex),一个数值变量(Height)作统计汇总
对一个分组变量(Sex),两个数值变量(Height,Weight)作统计汇总
对两个分组变量(Sex,Age),两个数值变量(Height,Weight)作统计汇总
很显然这样的结果并不像Excel中预期的那样,该如何变成列联表的形式的?很简单,只需将结果进行非堆叠操作(unstack)即可:
看,这样的结果是不是比上面那种看起来更舒服一点?
使用多个聚合函数
有关更多数据透视表的操作,可参考《Pandas透视表(pivot_table)详解》一文,链接地址:http://python.jobbole.com/81212/
八、多层索引的使用
最后我们再来讲讲pandas中的一个重要功能,那就是多层索引。在序列中它可以实现在一个轴上拥有多个索引,就类似于Excel中常见的这种形式:
对于这样的数据格式有什么好处呢?pandas可以帮我们实现用低维度形式处理高维数数据,这里举个例子也许你就能明白了:
对于这种多层次索引的序列,取数据就显得非常简单了:
对于这种多层次索引的序列,我们还可以非常方便的将其转换为数据框的形式:
以上针对的是序列的多层次索引,数据框也同样有多层次的索引,而且每条轴上都可以有这样的索引,就类似于Excel中常见的这种形式:
我们不妨构造一个类似的高维数据框:
同样,数据框中的多层索引也可以非常便捷的取出大块数据:
在数据框中使用多层索引,可以将整个数据集控制在二维表结构中,这对于数据重塑和基于分组的操作(如数据透视表的生成)比较有帮助。
就拿student二维数据框为例,我们构造一个多层索引数据集:
讲到这里,我们关于pandas模块的学习基本完成,其实在掌握了pandas这8个主要的应用方法就可以灵活的解决很多工作中的数据处理、统计分析等任务。有关更多的pandas介绍,可参考pandas官方文档:http://pandas.pydata.org/pandas-docs/version/0.17.0/whatsnew.html。 ####感谢刘顺祥作者分享……Y(^_^)Y####