74

假设我有一个 MultiIndex 系列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)这样的功能?进行这种操作的推荐方法是什么?我希望获得一个新系列,其中该函数产生的值应用于每一行和相同的 MultiIndex。

4

6 回答 6

50

我不相信apply可以访问索引;如您所见,它将每一行视为一个 numpy 对象,而不是一个系列:

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

于 2013-08-19T14:52:38.490 回答
17

让它成为一个框架,如果你想返回标量(所以结果是一个系列)

设置

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
于 2013-08-19T15:04:10.303 回答
12

转换为DataFrame并沿行应用。您可以将索引作为x.name. x也是一个Series值为 1 的 now

s.to_frame(0).apply(f, axis=1)[0]
于 2017-12-05T03:54:31.197 回答
3

可能会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

我建议测试速度(因为应用效率取决于功能)。虽然,我发现applys 更具可读性......

于 2013-08-19T15:51:26.827 回答
0

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

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)
于 2015-06-16T23:22:00.750 回答
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

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

于 2019-01-03T16:31:27.363 回答