实话实说,我一贯不太喜欢Pandas,由于它的功能实在太过强大了,想要熟练地驾驭它,对于我这样的中老年人来讲,学习成本偏高。不过,对于接受能力超强的年轻人而言,Pandas确实是数据处理方面不可或缺的利器,个人子侄辈中就有多人喜欢使用。正是由于他们在Pandas的使用过程当中,不断地向我咨询问题,我在帮他们解决问题的过程当中,也逐渐熟悉了Pandas。这不,今天中午又有问题提出来了,此次是一个很是经典的问题,几乎每一个人都会遇到。能够说,学会了解决这个问题,才算真正理解了Pandas。我把这个问题产生的背景、缘由和解决方案,尽量用浅显的文字完整地记录在这里,但愿这一篇文章可以成为Pandas的入门读物。更详细的教程,请参考个人另外一篇博客《Pandas简明教程》。html
DataFrame是Pandas最核心最经常使用的数据结构,能够理解为二维的异构表格。所谓异构,是指DataFrame的每一列均可以拥有独立的数据类型,而没必要像Numpy的多维数组(ndarray)那样全部元素必须是同一种数据类型。DataFrame的每一列都有一个列名,每一行都有一个索引。python
有多种方式能够建立DataFrame对象,将字典数据转换为DataFrame对象是最多见的建立方法,字典的键对应的是DataFrame的列,键名自动称为列名。若是没有指定索引,则使用默认索引(从0开始的连续整数)。web
>>> import pandas as pd >>> data = { '华东科技': [1.91, 1.90, 1.86, 1.84], '长安汽车': [11.27, 11.14, 11.28, 11.71], '西藏矿业': [7.89, 7.79, 7.61, 7.50], '重庆啤酒': [50.46, 50.17, 50.28, 50.28] } >>> df = pd.DataFrame(data) >>>> df 华东科技 长安汽车 西藏矿业 重庆啤酒 0 1.91 11.27 7.89 50.46 1 1.90 11.14 7.79 50.17 2 1.86 11.28 7.61 50.28 3 1.84 11.71 7.50 50.28
Pandas的条件检索很是灵活,下面的代码演示了最经常使用的几种方式。数组
>>> df[df.长安汽车 > 11.2] # 检索长安汽车股价大于11.2的全部行 华东科技 长安汽车 西藏矿业 重庆啤酒 0 1.91 11.27 7.89 50.46 2 1.86 11.28 7.61 50.28 3 1.84 11.71 7.50 50.28 >>> df[(df.长安汽车 > 11.2) & (df.华东科技 < 1.9)] # 检索知足“与”条件的全部行 华东科技 长安汽车 西藏矿业 重庆啤酒 2 1.86 11.28 7.61 50.28 3 1.84 11.71 7.50 50.28 >>> df[df.西藏矿业.isin([7.61, 7.89])] # 检索西藏矿业股价等于多个指定值的全部行 华东科技 长安汽车 西藏矿业 重庆啤酒 0 1.91 11.27 7.89 50.46 2 1.86 11.28 7.61 50.28 >>> df[df.index.isin([1,3])] # 检索索引号等于指定值的全部行 华东科技 长安汽车 西藏矿业 重庆啤酒 1 1.90 11.14 7.79 50.17 3 1.84 11.71 7.50 50.28
Pandas的条件检索,本质上和Numpy是同样的,返回的是布尔型的结果,咱们再用这个布尔型的结果去索引,获得了检索结果。数据结构
>>> df.长安汽车 > 11.2 0 True 1 False 2 True 3 True Name: 长安汽车, dtype: bool
一样,使用Numpy的取反符号(~)能够反向选取检索结果。ide
>>> df[~df.index.isin([1,3])] # 取反 华东科技 长安汽车 西藏矿业 重庆啤酒 0 1.91 11.27 7.89 50.46 2 1.86 11.28 7.61 50.28
对于检索到的结果数据,若是想修改的话,好比改为无效值nan(须要提早导入Numpy),通常会写成这样:svg
>>> import numpy as np >>> df[~df.index.isin([1,3])].iloc[:,:] = np.nan
然而,这样倒是行不通的。有趣的是,这不是一个错误,而是警告。到这里,恭喜你,经典的SettingWithCopyWarning问题终于出现了!解决了它,你就能够步入Pandas高手之列了。学习
Warning (from warnings module): File "C:\Users\xufive\AppData\Local\Programs\Python\Python37\lib\site-packages\pandas\core\indexing.py", line 671 self._setitem_with_indexer(indexer, value) SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy Warning (from warnings module): File "__main__", line 1 SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
是使用loc选取数据的方法错误吗?显然不是,由于用loc直接选取df的数据作修改是没有任何问题的。测试
>>> df.iloc[:,:] = np.nan >>> df 华东科技 长安汽车 西藏矿业 重庆啤酒 0 NaN NaN NaN NaN 1 NaN NaN NaN NaN 2 NaN NaN NaN NaN 3 NaN NaN NaN NaN
虽然检索结果看起来也是一个DataFrame,但对检索结果再使用loc选取并修改数据,就出现了问题。原始的DataFrame和做为检索结果的DataFrame有什么不一样呢?ui
原来,这是Pandas的针对链式赋值(Chained Assignment)的保护机制致使的结果。所谓链式赋值,就是对索引的索引结果赋值。当咱们使用条件检索时,至关于一次索引,在对这个结果作loc选取,就是二次索引,也就是链式索引,而链式索引在Pandas体系中被禁止赋值。简单理解,就是咱们没法对经过两个方括号选取的数据赋值。
了解问题产生的缘由,就容易找到解决方案了:用检索所条件做为loc的行参数,将两次索引变成一次,天然就没有链式索引,赋值也就再也不受限了。如下是完整代码。
>>> import pandas as pd >>>> import numpy as np >>> data = { '华东科技': [1.91, 1.90, 1.86, 1.84], '长安汽车': [11.27, 11.14, 11.28, 11.71], '西藏矿业': [7.89, 7.79, 7.61, 7.50], '重庆啤酒': [50.46, 50.17, 50.28, 50.28] } >>> df = pd.DataFrame(data) >>> df 华东科技 长安汽车 西藏矿业 重庆啤酒 0 1.91 11.27 7.89 50.46 1 1.90 11.14 7.79 50.17 2 1.86 11.28 7.61 50.28 3 1.84 11.71 7.50 50.28 >>> df.loc[~df.index.isin([1,3]), :] = np.nan >>> df 华东科技 长安汽车 西藏矿业 重庆啤酒 0 NaN NaN NaN NaN 1 1.90 11.14 7.79 50.17 2 NaN NaN NaN NaN 3 1.84 11.71 7.50 50.28
loc的列参数,除了冒号(:)指定所有列,也能够用列名指定单列,或者用元组指定多列。
>>> df = pd.DataFrame(data) >>> df 华东科技 长安汽车 西藏矿业 重庆啤酒 0 1.91 11.27 7.89 50.46 1 1.90 11.14 7.79 50.17 2 1.86 11.28 7.61 50.28 3 1.84 11.71 7.50 50.28 >>> df.loc[df.长安汽车 > 11.2, ('华东科技', '西藏矿业', '重庆啤酒')] = np.nan >>> df 华东科技 长安汽车 西藏矿业 重庆啤酒 0 NaN 11.27 NaN NaN 1 1.9 11.14 7.79 50.17 2 NaN 11.28 NaN NaN 3 NaN 11.71 NaN NaN
玩转Pandas就是这么简单,你get到了吗?