Индекс доступа в pandas.Series.apply
Допустим, у меня есть серия 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.
Ответы
Ответ 1
Я не верю, что 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(s.reset_index().apply(f, axis=1).values, index=s.index)
Другие подходы могут использовать s.get_level_values
, который, по моему мнению, часто становится немного уродливым, или s.iterrows()
, который, вероятно, будет медленнее - возможно, в зависимости от того, что именно делает f
.
Ответ 2
Сделайте это фреймом, верните скаляры, если хотите (поэтому результат представляет собой серию)
Настройка
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
Ответ 3
Преобразуйте в DataFrame
и примените вдоль строки. Вы можете получить доступ к индексу как x.name
. x
также является Series
теперь с 1 значением
s.to_frame(0).apply(f, axis=1)[0]
Ответ 4
Скорее вы можете использовать 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
более читабельны...
Ответ 5
Вы можете получить доступ ко всей строке в качестве аргумента внутри fucntion, если вы используете 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)