Вибір зі складними критеріями від pandas.DataFrame


234

Наприклад, у мене простий DF:

import pandas as pd
from random import randint

df = pd.DataFrame({'A': [randint(1, 9) for x in xrange(10)],
                   'B': [randint(1, 9)*10 for x in xrange(10)],
                   'C': [randint(1, 9)*100 for x in xrange(10)]})

Чи можу я вибрати значення "A", для яких відповідні значення для "B" будуть більшими за 50, а для "C" - не рівні 900, використовуючи методи та ідіоми Pandas?


df.queryі, pd.evalздається, добре підходить для цього випадку використання. Для отримання інформації про pd.eval()сімейство функцій, їх особливості та випадки використання, будь ласка, відвідайте Динамічну оцінку вираження в пандах за допомогою pd.eval () .
cs95

Можна також перевірити відповідь @Gecko у: stackoverflow.com/questions/13611065/…
Ніколас Хамфрі

Відповіді:


390

Звичайно! Налаштування:

>>> import pandas as pd
>>> from random import randint
>>> df = pd.DataFrame({'A': [randint(1, 9) for x in range(10)],
                   'B': [randint(1, 9)*10 for x in range(10)],
                   'C': [randint(1, 9)*100 for x in range(10)]})
>>> df
   A   B    C
0  9  40  300
1  9  70  700
2  5  70  900
3  8  80  900
4  7  50  200
5  9  30  900
6  2  80  700
7  2  80  400
8  5  80  300
9  7  70  800

Ми можемо застосувати операції зі стовпцями та отримати об'єкти булевих рядів:

>>> df["B"] > 50
0    False
1     True
2     True
3     True
4    False
5    False
6     True
7     True
8     True
9     True
Name: B
>>> (df["B"] > 50) & (df["C"] == 900)
0    False
1    False
2     True
3     True
4    False
5    False
6    False
7    False
8    False
9    False

[Оновити, щоб перейти на новий стиль .loc]:

І тоді ми можемо використовувати їх для індексації в об’єкт. Для доступу до читання можна ланцюжкові індекси:

>>> df["A"][(df["B"] > 50) & (df["C"] == 900)]
2    5
3    8
Name: A, dtype: int64

але ви можете потрапити в проблеми через різницю між переглядом і копією, яка робить це для доступу до запису. Ви можете використовувати .locнатомість:

>>> df.loc[(df["B"] > 50) & (df["C"] == 900), "A"]
2    5
3    8
Name: A, dtype: int64
>>> df.loc[(df["B"] > 50) & (df["C"] == 900), "A"].values
array([5, 8], dtype=int64)
>>> df.loc[(df["B"] > 50) & (df["C"] == 900), "A"] *= 1000
>>> df
      A   B    C
0     9  40  300
1     9  70  700
2  5000  70  900
3  8000  80  900
4     7  50  200
5     9  30  900
6     2  80  700
7     2  80  400
8     5  80  300
9     7  70  800

Зауважте, що я випадково набрав == 900і не != 900, або ~(df["C"] == 900), але я лінивий, щоб виправити це. Вправа для читача. : ^)


5
Про .locоновлення - було б добре, якщо ви уточнили, де ми отримуємо копію та де перегляд.
Gill Bates

3
чи можна фільтрувати фрейм даних панди та використовувати оператор АБО. Наприклад, якщо був місяць стовпця, чи можете ви сказати df = data ['month' == JAN OR 'month' == FEB]? І, можливо, включіть другий стовпчик, що робить запит складнішим, newdf, де col_month = січень АБО feb AND col_day = ПОНЕДІЛНИК або ВНІЧНЯ
yoshiserry

7
@yoshiserry: будь ласка, задайте це окремо. Тут ніхто цього не побачить у коментарях до старої відповіді.
DSM

2
Не забувайте дужки - у вас з’являться дивні помилки на кшталт{TypeError}cannot compare a dtyped [int64] array with a scalar of type [bool]
Mr_and_Mrs_D

Чи не це використання дужок призводить до розрахунків для всієї серії? Що робити, якщо ми хочемо кілька разів підвищувати ефективність?
ifly6

56

Іншим рішенням є використання методу запиту :

import pandas as pd

from random import randint
df = pd.DataFrame({'A': [randint(1, 9) for x in xrange(10)],
                   'B': [randint(1, 9) * 10 for x in xrange(10)],
                   'C': [randint(1, 9) * 100 for x in xrange(10)]})
print df

   A   B    C
0  7  20  300
1  7  80  700
2  4  90  100
3  4  30  900
4  7  80  200
5  7  60  800
6  3  80  900
7  9  40  100
8  6  40  100
9  3  10  600

print df.query('B > 50 and C != 900')

   A   B    C
1  7  80  700
2  4  90  100
4  7  80  200
5  7  60  800

Тепер, якщо ви хочете змінити повернені значення у стовпці А, ви можете зберегти їх індекс:

my_query_index = df.query('B > 50 & C != 900').index

.... і використовуйте .ilocдля їх зміни, тобто:

df.iloc[my_query_index, 0] = 5000

print df

      A   B    C
0     7  20  300
1  5000  80  700
2  5000  90  100
3     4  30  900
4  5000  80  200
5  5000  60  800
6     3  80  900
7     9  40  100
8     6  40  100
9     3  10  600

12

І не забудьте використовувати дужки!

Майте на увазі, що &оператор має перевагу над такими операторами, як >і <т. Д. Саме тому

4 < 5 & 6 > 4

оцінює до False. Тому, якщо ви використовуєте pd.loc, вам потрібно поставити дужки навколо своїх логічних висловлювань, інакше ви отримаєте помилку. Ось чому:

df.loc[(df['A'] > 10) & (df['B'] < 15)]

замість

df.loc[df['A'] > 10 & df['B'] < 15]

що призведе до

TypeError: не може порівнювати масив dtyped [float64] зі скаляром типу [bool]


3

Для порівняння можна використовувати панди, в яких є деякі вбудовані функції. Отже, якщо ви хочете вибрати значення "A", які відповідають умовам "B" і "C" (якщо припустити, що ви хочете повернути об'єкт панд DataFrame)

df[['A']][df.B.gt(50) & df.C.ne(900)]

df[['A']] поверне вам колонку A у форматі DataFrame.

функція pandas 'gt' поверне позиції стовпця B, які перевищують 50, а 'ne' поверне позиції, не рівні 900.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.