熊猫的访问索引

假设我有一个 MultiIndex Series s:

>>> s
values
a b
1 2  0.1
3 6  0.3
4 4  0.7

我想应用一个函数它使用行的索引:

def f(x):
# conditions or computations using the indexes
if x.index[0] and ...:
other = sum(x.index) + ...
return something

如何为这样的函数执行 s.apply(f)?进行这种操作的推荐方法是什么?我希望获得一个新的 Series,其中该函数产生的值应用于每一行和相同的 MultiIndex。

62971 次浏览

我不相信 apply可以访问索引; 它把每一行当作一个数字对象,而不是一个 Series,正如你所看到的:

In [27]: s.apply(lambda x: type(x))
Out[27]:
a  b
1  2    <type 'numpy.float64'>
3  6    <type 'numpy.float64'>
4  4    <type 'numpy.float64'>

要绕过这个限制,可以将索引提升到列,应用函数,并使用原始索引重新创建 Series。

Series(s.reset_index().apply(f, axis=1).values, index=s.index)

其他方法可以使用 s.get_level_values,在我看来,它通常有点难看,或者使用 s.iterrows(),它可能会慢一些——也许这取决于 f具体做什么。

将它设置为一个框架,如果需要,可以返回标量(因此结果是一个序列)

设置

In [11]: s = Series([1,2,3],dtype='float64',index=['a','b','c'])


In [12]: s
Out[12]:
a    1
b    2
c    3
dtype: float64

打印功能

In [13]: def f(x):
print type(x), x
return x
....:


In [14]: pd.DataFrame(s).apply(f)
<class 'pandas.core.series.Series'> a    1
b    2
c    3
Name: 0, dtype: float64
<class 'pandas.core.series.Series'> a    1
b    2
c    3
Name: 0, dtype: float64
Out[14]:
0
a  1
b  2
c  3

因为您可以在这里返回任何内容,所以只需返回标量(通过 name属性访问索引)

In [15]: pd.DataFrame(s).apply(lambda x: 5 if x.name == 'a' else x[0] ,1)
Out[15]:
a    5
b    2
c    3
dtype: float64

你会发现在这里使用 where比使用 apply更快:

In [11]: s = pd.Series([1., 2., 3.], index=['a' ,'b', 'c'])


In [12]: s.where(s.index != 'a', 5)
Out[12]:
a    5
b    2
c    3
dtype: float64

你也可以对任何部分使用 numpy 风格的逻辑/函数:

In [13]: (2 * s + 1).where((s.index == 'b') | (s.index == 'c'), -s)
Out[13]:
a   -1
b    5
c    7
dtype: float64


In [14]: (2 * s + 1).where(s.index != 'a', -s)
Out[14]:
a   -1
b    5
c    7
dtype: float64

我建议测试速度(因为对应用的效率将取决于功能)。虽然,我发现 apply更易读..。

如果使用 DataFrame.application ()而不是 Series.application () ,则可以将整行作为函数内部的参数访问。

def f1(row):
if row['I'] < 0.5:
return 0
else:
return 1


def f2(row):
if row['N1']==1:
return 0
else:
return 1


import pandas as pd
import numpy as np
df4 = pd.DataFrame(np.random.rand(6,1), columns=list('I'))
df4['N1']=df4.apply(f1, axis=1)
df4['N2']=df4.apply(f2, axis=1)

转换为 DataFrame并沿行应用。您可以以 x.name的形式访问索引。x现在也是一个值为1的 Series

s.to_frame(0).apply(f, axis=1)[0]

使用 reset_index()将 Series 转换为 DataFrame,将索引转换为列,然后使用 apply将函数转换为 DataFrame。

棘手的部分是知道 reset_index()如何命名这些列,所以这里有几个示例。

单索引系列

s=pd.Series({'idx1': 'val1', 'idx2': 'val2'})


def use_index_and_value(row):
return 'I made this with index {} and value {}'.format(row['index'], row[0])


s2 = s.reset_index().apply(use_index_and_value, axis=1)


# The new Series has an auto-index;
# You'll want to replace that with the index from the original Series
s2.index = s.index
s2

产出:

idx1    I made this with index idx1 and value val1
idx2    I made this with index idx2 and value val2
dtype: object

使用多索引系列

这里的概念相同,但是您需要以 row['level_*']的形式访问索引值,因为 Series.reset_index()将它们放置在这里。

s=pd.Series({
('idx(0,0)', 'idx(0,1)'): 'val1',
('idx(1,0)', 'idx(1,1)'): 'val2'
})


def use_index_and_value(row):
return 'made with index: {},{} & value: {}'.format(
row['level_0'],
row['level_1'],
row[0]
)


s2 = s.reset_index().apply(use_index_and_value, axis=1)


# Replace auto index with the index from the original Series
s2.index = s.index
s2

产出:

idx(0,0)  idx(0,1)    made with index: idx(0,0),idx(0,1) & value: val1
idx(1,0)  idx(1,1)    made with index: idx(1,0),idx(1,1) & value: val2
dtype: object

如果您的系列或索引具有名称,则需要相应地进行调整。

Series实现了 items()方法,该方法允许使用列表理解来映射键(即索引值)和值。

给出一个系列:

In[1]: seriesA = pd.Series([4, 2, 3, 7, 9], name="A")
In[2]: seriesA
Out[2]:
0    4
1    2
2    3
3    7
4    9
dtype: int64

现在,假设函数 f接受一个键和一个值:

def f(key, value):
return key + value

现在我们可以通过使用 for 理解来创建一个新的系列:

In[1]: pd.Series(data=[f(k,v) for k, v in seriesA.items()], index=seriesA.index)
Out[1]:
0     4
1     3
2     5
3    10
4    13
dtype: int64

当然,这并没有利用任何麻烦的性能优势,但是对于某些操作来说,这是有意义的。