Подтвердить что ты не робот

Получить определенную строку как серию из pandas dataframe

Как мы можем получить определенную отфильтрованную строку как серию?

Пример dataframe:

>>> df = pd.DataFrame({'date': [20130101, 20130101, 20130102], 'location': ['a', 'a', 'c']})
>>> df
       date location
0  20130101        a
1  20130101        a
2  20130102        c

Мне нужно выбрать строку, в которой location есть c как серия.

Я пробовал:

row = df[df["location"] == "c"].head(1)  # gives a dataframe
row = df.ix[df["location"] == "c"]       # also gives a dataframe with single row

В обоих случаях я не могу использовать ряд как строку.

4b9b3361

Ответ 1

Используйте функцию squeeze, которая удалит одно измерение из кадра данных:

df[df["location"] == "c"].squeeze()
Out[5]: 
date        20130102
location           c
Name: 2, dtype: object

DataFrame.squeeze метод действует один и тот же путь squeeze аргумента read_csv функции, если задано значение True: если в результате dataframe является 1-Len dataframe, т.е. она имеет только одно измерение (столбец или строку), то объект сжал до объекта меньшего размера.

В вашем случае вы получаете объект Series из DataFrame. Та же логика применяется, если вы сжимаете Panel вниз в DataFrame.

squeeze явно присутствует в вашем коде и ясно показывает ваше намерение "бросить" объект в руки, потому что его размерность можно спроецировать на меньший.

Если в кадре данных более одного столбца или строки, сжатие не имеет никакого эффекта.

Ответ 2

Вы можете просто взять первую строку с целым индексированием (функция iloc()):

>>> df[df["location"] == "c"].iloc[0]
date        20130102
location           c
Name: 2, dtype: object

Ответ 3

Как я могу получить конкретную строку в виде серии из pandas DataFrame?

Надежное решение: DataFrame.iloc с Series.idxmax

В качестве лучшей альтернативы, если вы можете гарантировать, что хотя бы одна строка соответствует условию, используйте Series.idxmax() для маски и сделайте это с помощью одного вызова DataFrame.iloc.

df.iloc[(df['location'] == 'c').idxmax()]

date        20130102
location           c
Name: 2, dtype: object

Возможно, это лучшая альтернатива текущим опубликованным ответам, потому что она гарантирует возврат одной (и только одной) строки, а также никогда не делает копию.


Критика других ответов

В принятом ответе только мимоходом было упомянуто, что squeeze имеет никакого эффекта, если возвращено более одной строки, но это проблема

df

       date location
0  20130101        a
1  20130101        a
2  20130102        c

df[df["location"] == "c"].squeeze()   # Works as expected.

date        20130102
location           c
Name: 2, dtype: object

Теперь рассмотрим, когда более чем один ряд соответствует этому условию.

df2 = pd.concat([df] * 2, ignore_index=True)
df2

       date location
0  20130101        a
1  20130101        a
2  20130102        c
3  20130101        a
4  20130101        a
5  20130102        c

df2[df2["location"] == "c"].squeeze() # No effect.

       date location
2  20130102        c
5  20130102        c

При использовании idxmax индекс первой строки с наибольшим значением в результате "location"] == "c" (что соответствует True если хотя бы одна строка удовлетворяет условию). Таким образом, вы получаете серию каждый раз.

Далее, ответ @RomanPekar использует iloc в результате логического вызова индексации, который может возвращать или не возвращать копию. Не говоря уже о том, что это становится проблемой, если вы пытаетесь назначить новую строку обратно:

df[df["location"] == "c"].iloc[0] = pd.Series({'location': 'd', 'date': np.nan})
# SettingWithCopyWarning: 
# A value is trying to be set on a copy of a slice from a DataFrame.
# Try using .loc[row_indexer,col_indexer] = value instead

Вы получаете SettingWithCopyWarning (вы можете прочитать об этом здесь).

Это не проблема, если вы используете один вызов iloc:

df.iloc[(df['location'] == 'c').idxmax()] = (
    pd.Series({'location': 'd', 'date': np.nan}))
df

         date location
0  20130101.0        a
1  20130101.0        a
2         NaN        d

Пусть покупатель будет бдителен

idxmax вернет индекс первой строки, который является True в результате df['location'] == 'c':

df2.iloc[(df2['location'] == 'c').idxmax()]

date        20130102
location           c
Name: 2, dtype: object

Но предостережение здесь видно, когда вообще нет рядов. idxmax всегда будет просто возвращать индекс первой строки (поскольку значение в первой строке равно False, наибольшее значение в маске).

df3 = df.query('location == "a"')
df3

       date location
0  20130101        a
1  20130101        a

# This will produce an incorrect result.
df3.iloc[(df3['location'] == 'c').idxmax()]  

date        20130101
location           a
Name: 0, dtype: object

Таким образом, вы можете добавить код обработки ошибок для обработки этих угловых случаев. Мое предложение - это встроенное утверждение if-else для краткости:

df3.iloc[mask.idxmax()] if mask.any() else None

Некоторые примеры,

# Correct handling of corner case.
m = df3['location'] == 'c'
ser = df3.iloc[m.idxmax()] if m.any() else None
print(ser)
# None

# Correct handling of the standard case.
m = df3['location'] == 'a'
df3.iloc[m.idxmax()] if m.any() else None

date        20130101
location           a
Name: 0, dtype: object