提示:Python和bumpy的索引操作[ ]和属性操作. 为pandas数据结构提供了非常快速和简便的方式。如果你已经知道如何操作Python字典和Numpy数组的话,这就没什么新的了。然而,由于数据的类型无法提前预知,直接使用标准操作将会有一些优化的限制。对于产品代码来说,我们建议你可以利用本文展示的优化的pandas数据使用方法。
Object Type | Indexers |
---|---|
Series | s.loc[indexer] |
DataFrame | df.loc[row_indexer,column_indexer] |
Panel | p.loc[item_indexer,major_indexer,minor_indexer] |
对象类型 | 选取 | 返回值类型 |
---|---|---|
Series | series[label] |
标量值 |
DataFrame | frame[colname] |
对应colname的Series |
Panel | panel[itemname] |
对应itemname的DataFrame |
警告:当从.loc, .iloc和.ix设置Series和DataFrame时,pandas会将所有轴对齐。
这不会改变df,因为在赋值之前就进行了列对齐。
正确的做法是使用原始值
警告:
你也可以向一个DataFrame的一行分配一个dict。
警告:对于一个设置操作,是返回一个复制还是引用取决于当时的上下文。这叫做“链式赋值”,这种情况应当避免。See Returning a View versus Copy
警告:当你的切片器与索引类型不兼容(或不可转换)时,.loc是非常严格的。例如在一个DatatimeIndex中使用整数的话,将会引发一个TypeError。
在切片中的string能够转换为index的类型,这样才能正常切片。
警告:对于一个设置操作,是返回一个复制还是引用取决于当时的上下文。这叫做“链式赋值”,这种情况应当避免。
pandas提供了一系列的方法来基于整数进行索引。该语法紧跟Python和numpy切片。这些是0-based索引。切片时,范围内的第一个和最后一个都包含在内。如果试图使用非整数,即使是一个有效的标签也将会引发一个IndexError。 See Returning a View versus Copy
正如在Python或Numpy中那样,超出索引范围的切片也是被允许的。
注意:在0.14.0以前的版本中,iloc不能接收超出索引范围的切片,例如超出索引对象长度的值就不可以。
注意到这样做会导致一个空轴(如返回一个空的DataFrame)
一个单独的超出范围的索引器将会引发IndexError. 一个其元素超出了索引范围的索引列表也会引发IndexError。
你可以在Series中使用可调用函数
使用这种方法/索引时,你可以不用临时变量就能够进行数据选择操作。
这就像是DataFrame的一个追加操作。
由于使用[ ]进行索引必须管理很多情况(单标签访问,切片,布尔索引等等),它会花费一些性能来识别你究竟请求的是什么。如果你想要访问一个标量,最快速的方法是使用at和iat方法,他们能够适用于所有数据结构。
at提供基于标签的标量查找(与loc类似),而iat提供基于整数的标量查找(与iloc类似)。
你也可以使用同样的索引进行设置
如果索引缺失的话,at方法可对对象进行原地扩充
另一个常见的操作是使用布尔向量来过滤数据。操作是|对应或,&对应与,~对应非。这些必须使用括号进行分组。
使用一个布尔向量来对一个Series进行索引与对一个numpy的多维数组进行索引是一样一样的:
你可以使用一个与DataFrame的index相同长度的布尔向量从一个DataFrame中选择列(例如,一些来自DataFrame的一列的东西)
Series的list和map方法也可以用来产生更为复杂的匹配标准:
注意,使用布尔向量和其他索引表达式共同索引时,使用选择方法 通过标签选择,通过位置选择和先进索引,你可能会选出不只一个轴的数据。
考虑到Series的isin方法,它能够返回一个布尔向量,Series的元素在传递列表的地方显示为True。这使你能够选择那些一列或多列中有你需要的值的行。
该方法同样适用于Index对象,当你不知道那个标签是真的存在的时候,这种方法也同样适用。
另外,MultiIndex方法能够允许选取一个单独的level来用于成员资格审查
DataFrame也有isin方法。当使用isin时,需要传入一个值的集合,数组或字典都可以。如果是数组,isin返回一个布尔型的DataFrame,它和原DataFrame的shape一样,而且元素在值序列中的地方显示为True.
通常情况下,你会想要使用特定的列来匹配特定的值。只需要使值成为一个dict,其key是列,value是你想要检索的item列表即可。
将DataFrame的isin方法和any( )和all( )方法混合起来以一个给定的标准快速选择你的数据的子集。选择一行数据,它的每一列都有各自的标准:
使用一个布尔向量从一个Series中选取值通常会返回一个数据的子集。为了保证选取的输出与源数据有相同的规模,你可以使用Series和DataFrame中的where方法。
只返回选取的行:
返回一个与源数据具有相同规模的Series
使用一个布尔标准从一个DataFrame中选取值也能保留输入数据的规模。在底层使用where作为实现。等价于df.where(df<0)
另外,在返回的复制数据中,where需要一个可选的其他参数来当条件为假时进行替换。
你或许想要基于一些布尔标准来设置值。这能够直观地这样做:
默认情况下,where将会返回一个数据的修改副本。有一个可选择的参数inplace,它能够在源数据上直接修改,而不产生一个副本。
注意 DataFrame.where()和
numpy.where()的识别标志
不同。大体上,df1.where(m, df2)
等价于np.where(m, df1, df2)
.
校准
与此同时,where使输入的环境对齐(ndarray 或DataFrame),因此部分选择与设置是可能的。这与使用.ix进行部分设置相似(但是在内容上倒不如轴标签)
在使用where时,where也可以接收axis和level参数来使输入对齐。
这个方法与下面的方法相同,但是比下面的快。
where能够接收一个可调用函数作为条件和其他参数。这个函数必须有一个参数(Series 或者 DataFrame),并返回有效的输出作为条件或其他参数。
伪装
伪装是where的逆布尔运算。
query()
方法 (实验性的)DataFrame
对象有一个query()方法能够允许使用一个表达式来选取数据。
你可以获取到frame中列b中介于列a和列c之间的值,例如:
如果没有名为a的列,这样做的话将会把操作作用于一个命了名的index。
如果你不想或不能为你的index命名,你可以在你的query表达式中使用“index”这个名字。
注意 如果你的index的名字和一个列名相同,那列名将会被赋予优先值。如
即使index已经命名了,你还是可以在query表达式中使用“index”这个名字对index列进行使用:
如果由于某些原因你有一列名为index,那么你可以使用ilevel_0来使用index,但是这时你最好应该考虑给你的列换个名字。
MultiIndex
的query()
语法你也可以把 MultiIndex
和一个DataFrame的levels当做frame中的列进行使用。
如果 MultiIndex的levels未命名,你可以使用特殊名字来使用它们:
惯例是level_0,它意味着为index的第0level“索引level0”
query()
使用示例一个query()的使用示例是,当你有一个有着共同列名(或索引level/名称)的子集DataFrame的对象集,你可以向所有frame传递相同的query,而不用指定哪个frame是你要查询的。
query()
Python与pandas语法比较完全的numpy形式的语法
去掉括号会更好一点 (通过绑定比较操作符&
/|
)
使用英语来代替符号
可能和你在纸上写的非常相近
in
和 not in
操作在比较操作中,query()也支持Python的特殊用法in和not in,为调用isin方法提供了一个简洁的语法。
你可以将这种方法和其他表达式混合来实现非常简洁的查询:
注意:in和not in在Python中进行了评估,因为numexpr没有与该操作相等的操作。然而,只有表达式中的in和not in自身在普通Python中被评估了。例如,在表达式
(b + c + d)通过numexpr进行评估,然后in操作就在普通Python评估了。通常来说,任何能够使用numexpr评估的操作都是这样。
==
操作和list对象的特殊用法使用==/!=对一列数值进行比较和in/not in的机制是相似的
你可以使用not或~操作来否定布尔表达式
当然,表达式也可以变得任意复杂
如果你想要识别并删除一个DataFrame中的重复行,有两种方法:duplicated和
drop_duplicates。每种都需要传入一个列参数来识别重复行。
默认情况下,几个重复行的第一行会被留下,但是每种方法都有一个keep参数来决定要留下哪一行。
keep='first'
(默认): 将第一行视为非重复行并留下keep='last'
:将最后一行视为非重复行并留下keep=False
: 删除所有行/将所有行都标记为重复行同样,你可以传入一个列来识别特定重复行
使用index.duplicated然后进行切片可以根据index的值去重。keep参数在这个方法中同样适用。
get()
方法Series, DataFrame, 和 Panel都有一个get方法能够返回一个默认值
select()
方法另一个从一个Series, DataFrame, 或 Panel对象中获取切片的方式是select方法。这个方法应该在只有当没有其他直接的方式可用时才用。select能够作用于轴标签并返回一个布尔值。例如:
lookup()
方法有些时候你想要按照某种特定顺序来获取行或列,那么lookup方法就能够实现,并返回一个numpy数组。例如,
pandas的Index类和它的子类可以被视为实施一个有序的多集。允许重复。然而,如果你试图将一个有重复项的索引对象转换为一个集合,将会引发一个异常。
Index
还为查找、数据规整和重新索引提供了必要的基础。创建一个Index的最简单的方式是向Index传递一个list或其他的序列。
你也可以给index起个名字,并存储在索引中:
如果名字是个集合,将会在控制台显示:
索引“大部分是不可变的”,但是设置和改变他们的元数据却是可能的,比如索引名(或者,对于MultiIndex来说,level和标签)
你可以使用rename,set_name ,set_levels set_labels来直接设置这些属性。它们默认返回一个副本,然而,你可以令关键字inplace=True来使数据直接原地改变。
MultiIndexes的 Advanced Indexing 用法。
set_names
, set_levels
, 和 set_labels
也有一个可选参数level
警告 在0.15.0版本中,为了在某些特定索引类型上的数值操作,+和-操作已经过时。+可以使用.union( )或 | 代替,-可以使用.difference( )代替。
The two main operations are These can be directly called as instance methods or used via overloaded operators. Difference is provided via the .difference()
method.
两个主要的操作是union (|)和
intersection (&)。它们可以作为实例方法被直接调用或通过重载操作使用。Difference 是由
.difference()方法实现的。
symmetric_difference (^)操作也同样能用,它能够返回idx1或idx2中出现的元素,但不能二者都返回。这等价于使用idx1.difference(idx2).union(idx2.difference(idx1))来创建索引,没有重复项。
重要: 虽然Index能够保留缺失值(NaN),但如果你不想出现什么乱七八糟的结果,最好还是避免缺失值。例如,有些操作会隐性地直接排除缺失值。
Index.fillna能够使用指定值来填充缺失值。
有时候你会向一个DataFrame中加载或创建一个数据集,并给这个后来加入的数据子集加上索引。有以下几种方法:
DataFrame有一个set_index方法,能够接收一个列名(对于常规Index来说)或一个列名list(对于MutiIndex来说)来创建一个新的,带有索引的DataFrame:
关键字append能够使你保留当前index,并把给定的列追加到一个MultiIndex中:
set_index中的其他选项能够使你保留索引列或者原地加上索引。
考虑到方便性,DataFrame有一个新的功能叫做reset_index,它能够把index值转换为DataFrame的列并设置一个简单的整数索引。它是set_index的逆运算。
输出的结果更像是一个SQL表或一个数组记录。来自index的列名被存储在names属性里。
你可以使用level关键字来移除index的一部分。
reset_index有一个可选参数drop,当drop=True时会直接丢弃index,而不是将index值放在DataFrame的列中。
注意 reset_index方法在以前的版本中叫做delevel
如果你自己创建了一个index,你可以将它分配到索引字段
在一个pandas对象中设置值时,必须小心避免链式索引的出现。这是个例子:
比较一下这两个访问方法:
这两种方法产生的结果一样,所以你该用哪一种?了解它们的操作顺序、了解为什么第二种方法(.loc)比第一种好得多,是非常有意义的。
dfmi [“one”] 选取了列的第一个level,并返回一个单独索引的DataFrame,另一个Python操作ddmi_with_one[“second”]选取“second”索引的Series。这由可变的dfmi_with_one来指示,因为pandas把这些操作视为独立事件。例如对__getitem__的单独调用,所以它必须把他们作为线性运算,他们一个接一个地发生。
相反,df。loc[:,(“one”,”second”)]向单独调用的__getitem__传入了一个嵌套元组(slice(None),('one','second’)).这能够使pandas把它们作为一个单一的实体进行处理。此外,这个操作命令比第一种方法要快得多,并且如果需要的话,还能够允许同时索引多个轴。
前面部分的问题仅仅是性能问题。为什么会有SettingWithCopy警告?当你的操作会多花费不必要的几毫秒时,我们通常不会报出警告!
但是事实证明,链式索引会导致不可预知的结果。要了解这一点,想想Python解释器如果执行这个代码的:
但这个代码的处理方式是完全不同的:
看到这里的__getitem__了吗?除了一些简单的情况之外,我们很难预测它到底会返回一个视图还是一个副本(这取决于数组的内存布局,pandas可不能保证这个),也不能预测__setitem__是将会直接修改dfmi还是修改一个用完即扔的临时对象。这也是SettingWithCopy在警告你的东西!
注意 你可能会想在第一个例子中我们是否应该考虑到loc的特性。但是我们能肯定在修改索引时,dfmi.loc是dfmi自身,所以dfmi.loc.__getitem__
/dfmi.loc.__setitem__操作是直接作用在dfmi自身上的。当然,
dfmi.loc.__getitem__ (idx)或许是dfmi的一个视图或副本。
有些时候,当没有明显的链式索引时,SettingWithCopy警告也会产生。这是SettingWithCopy的一些设计上的bug,pandas可能会试着发出警告,如果你这样做的话:
唉!真无奈啊!
此外,在链式表达式中,命令将决定是否返回一个副本。如果一个表达式会在一个副本或切片上设置值,那势必会引发一个SettingWithCopy异常(0.13.0以后的版本)
你可以通过选项mode.chained_assignment来控制一个链式分配,它能够接收值['raise','warn',None]。
然而这是在副本上的操作,并木有什么卵用。
一个链式分配也可以在设置一个混合类型的frame时让人猝不及防地出现。
注意 这些设置规则对.loc/.iloc/.ix通用
这才是正确的访问方法
下面的方法有时能够正常使用,但不能保证任何时候都正常,所以应该避免使用
下面的方法是错的,所以别用啊
警告 链式分配警告/异常是为了把可能无效的分配告知用户。有时候有可能是误报。