Як вибрати рядки на DataFrame
основі значень у деякому стовпці в Python Pandas?
У SQL я б використовував:
SELECT *
FROM table
WHERE colume_name = some_value
Я спробував переглянути документацію панд, але не одразу знайшов відповідь.
Як вибрати рядки на DataFrame
основі значень у деякому стовпці в Python Pandas?
У SQL я б використовував:
SELECT *
FROM table
WHERE colume_name = some_value
Я спробував переглянути документацію панд, але не одразу знайшов відповідь.
Відповіді:
Щоб вибрати рядки, значення стовпців яких дорівнює скаляру some_value
, використовуйте ==
:
df.loc[df['column_name'] == some_value]
Щоб вибрати рядки, значення стовпців яких є ітерабельним,, some_values
використовуйте isin
:
df.loc[df['column_name'].isin(some_values)]
Поєднайте кілька умов із &
:
df.loc[(df['column_name'] >= A) & (df['column_name'] <= B)]
Зверніть увагу на дужки. Завдяки правилам пріоритетності оператора Python , &
зв'язується більш щільно, ніж <=
та >=
. Таким чином, дужки в останньому прикладі необхідні. Без дужок
df['column_name'] >= A & df['column_name'] <= B
розбирається як
df['column_name'] >= (A & df['column_name']) <= B
що призводить до істинного значення серії, є неоднозначною помилкою .
Щоб вибрати рядки, значення стовпців яких не дорівнює some_value
, використовуйте !=
:
df.loc[df['column_name'] != some_value]
isin
повертає логічне значення серії, тому , щоб вибрати рядки , в яких значення НЕ в some_values
, звести на немає булева Series з допомогою ~
:
df.loc[~df['column_name'].isin(some_values)]
Наприклад,
import pandas as pd
import numpy as np
df = pd.DataFrame({'A': 'foo bar foo bar foo bar foo foo'.split(),
'B': 'one one two three two two one three'.split(),
'C': np.arange(8), 'D': np.arange(8) * 2})
print(df)
# A B C D
# 0 foo one 0 0
# 1 bar one 1 2
# 2 foo two 2 4
# 3 bar three 3 6
# 4 foo two 4 8
# 5 bar two 5 10
# 6 foo one 6 12
# 7 foo three 7 14
print(df.loc[df['A'] == 'foo'])
врожайність
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Якщо у вас є кілька значень, які ви хочете включити, покладіть їх у список (або загалом, будь-який ітерабельний) та використовуйте isin
:
print(df.loc[df['B'].isin(['one','three'])])
врожайність
A B C D
0 foo one 0 0
1 bar one 1 2
3 bar three 3 6
6 foo one 6 12
7 foo three 7 14
Однак зауважте, що якщо ви хочете зробити це багато разів, то ефективніше спершу зробити індекс, а потім скористатися df.loc
:
df = df.set_index(['B'])
print(df.loc['one'])
врожайність
A C D
B
one foo 0 0
one bar 1 2
one foo 6 12
або, щоб включити кілька значень від використання індексу df.index.isin
:
df.loc[df.index.isin(['one','two'])]
врожайність
A C D
B
one foo 0 0
one bar 1 2
two foo 2 4
two foo 4 8
two bar 5 10
one foo 6 12
df.where(condition)
умова повинна мати таку ж форму, що і df
.
df[df['column_name'] == some_value]
працює, навіщо нам .loc
тут додавати ?
Існує кілька способів вибору рядків з кадру даних панди:
df[df['col'] == value
])df.iloc[...]
)df.xs(...)
)df.query(...)
APIНижче я показую вам приклади кожного з порадами, коли використовувати певні методики. Припустимо, нашим критерієм є стовпець 'A'
=='foo'
(Примітка щодо продуктивності. Для кожного базового типу ми можемо робити прості речі за допомогою API панд або ми можемо виходити за межі API, як правило numpy
, і прискорювати роботу.)
Налаштування
Перше, що нам знадобиться - це визначити умову, яка буде виступати нашим критерієм для вибору рядків. Почнемо з справи ОП column_name == some_value
та включимо деякі інші випадки загального використання.
Запозичення від @unutbu:
import pandas as pd, numpy as np
df = pd.DataFrame({'A': 'foo bar foo bar foo bar foo foo'.split(),
'B': 'one one two three two two one three'.split(),
'C': np.arange(8), 'D': np.arange(8) * 2})
... Булева індексація вимагає знайти справжнє значення 'A'
стовпця кожного рядка рівним 'foo'
, а потім використовувати ці значення істини, щоб визначити, які рядки потрібно зберегти. Як правило, ми б назвати цю серію, масив значень істинності mask
. Ми зробимо це і тут.
mask = df['A'] == 'foo'
Потім ми можемо використовувати цю маску для зрізу або індексації кадру даних
df[mask]
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Це один із найпростіших способів виконання цього завдання, і якщо продуктивність чи інтуїтивність не є проблемою, це повинен бути обраний вами метод. Однак якщо продуктивність викликає занепокоєння, то, можливо, ви захочете розглянути альтернативний спосіб створення mask
.
Позиційна індексація ( df.iloc[...]
) має свої випадки використання, але це не один із них. Для того, щоб визначити, куди слід нарізати, спочатку нам потрібно провести той же булевий аналіз, який ми робили вище. Це дозволяє нам виконати один додатковий крок для виконання того ж завдання.
mask = df['A'] == 'foo'
pos = np.flatnonzero(mask)
df.iloc[pos]
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Індексація міток може бути дуже зручною, але в цьому випадку ми знову робимо більше роботи без користі
df.set_index('A', append=True, drop=False).xs('foo', level=1)
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
df.query()
APIpd.DataFrame.query
це дуже елегантний / інтуїтивний спосіб виконання цього завдання, але часто повільніше. Однак якщо ви звернете увагу на терміни, наведені нижче, для великих даних запит є дуже ефективним. Більше, ніж стандартний підхід і такої ж величини, як моя найкраща пропозиція.
df.query('A == "foo"')
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Моя перевага - використовувати Boolean
mask
Фактичні покращення можна зробити, змінивши те, як ми створюємо своє Boolean
mask
.
mask
альтернатива 1
Використовуйте базовий numpy
масив і відмовляйтеся від накладних коштів створення іншогоpd.Series
mask = df['A'].values == 'foo'
У кінці я покажу більш повні тести часу, але лише погляньте на підвищення продуктивності, який ми отримуємо, використовуючи зразок кадру даних. Спочатку ми розглядаємо різницю у створенніmask
%timeit mask = df['A'].values == 'foo'
%timeit mask = df['A'] == 'foo'
5.84 µs ± 195 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
166 µs ± 4.45 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Оцінка масиву mask
з numpy
масивом ~ 30 разів швидше. Частково це пов'язано з тим, що numpy
оцінка часто буває швидшою. Частково це також зумовлено відсутністю накладних витрат, необхідних для побудови індексу та відповідного pd.Series
об'єкта.
Далі ми розглянемо терміни нарізки однієї mask
проти іншої.
mask = df['A'].values == 'foo'
%timeit df[mask]
mask = df['A'] == 'foo'
%timeit df[mask]
219 µs ± 12.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
239 µs ± 7.03 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Підвищення продуктивності не так виражене. Ми побачимо, чи не витримає це більш надійне тестування.
mask
альтернатива 2
Ми могли б також реконструювати кадр даних. Існує велике застереження при реконструкції фрейму даних - ви повинні подбати про те, dtypes
коли це робите!
Замість цього df[mask]
ми зробимо це
pd.DataFrame(df.values[mask], df.index[mask], df.columns).astype(df.dtypes)
Якщо кадр даних змішаного типу, що є нашим прикладом, тоді, коли ми отримуємо df.values
результуючий масив, dtype
object
і, отже, всі стовпці нового кадру даних будуть мати dtype
object
. Таким чином, вимагають astype(df.dtypes)
і вбивають будь-які потенційні підвищення продуктивності.
%timeit df[m]
%timeit pd.DataFrame(df.values[mask], df.index[mask], df.columns).astype(df.dtypes)
216 µs ± 10.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
1.43 ms ± 39.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Однак якщо кадр даних не змішаного типу, це дуже корисний спосіб зробити це.
Дано
np.random.seed([3,1415])
d1 = pd.DataFrame(np.random.randint(10, size=(10, 5)), columns=list('ABCDE'))
d1
A B C D E
0 0 2 7 3 8
1 7 0 6 8 6
2 0 2 0 4 9
3 7 3 2 4 3
4 3 6 7 7 4
5 5 3 7 5 9
6 8 7 6 4 7
7 6 2 6 6 5
8 2 8 7 5 8
9 4 7 6 1 5
%%timeit
mask = d1['A'].values == 7
d1[mask]
179 µs ± 8.73 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Проти
%%timeit
mask = d1['A'].values == 7
pd.DataFrame(d1.values[mask], d1.index[mask], d1.columns)
87 µs ± 5.12 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Ми скорочуємо час навпіл.
mask
Альтернатива 3
@unutbu також показує нам, як використовувати pd.Series.isin
для обліку кожного елемента, df['A']
що знаходиться в наборі значень. Це визначає те саме, якщо наш набір значень є набором одного значення, а саме 'foo'
. Але він також узагальнює, якщо потрібно, включати більші набори значень. Виявляється, це все ще досить швидко, хоча це більш загальне рішення. Єдина реальна втрата - це інтуїтивність для тих, хто не знайомий з цією концепцією.
mask = df['A'].isin(['foo'])
df[mask]
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Однак, як і раніше, ми можемо використати numpy
для підвищення продуктивності, при цьому не жертвуючи практично нічого. Ми використаємоnp.in1d
mask = np.in1d(df['A'].values, ['foo'])
df[mask]
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Терміни
Я включатиму інші поняття, згадані в інших посадах, а також для довідок.
Код нижче
Кожен стовпець у цій таблиці являє собою кадр даних різної довжини, на якому ми перевіряємо кожну функцію. Кожен стовпець показує відносний час, зайнятий, при цьому найшвидша функція задається базовим індексом 1.0
.
res.div(res.min())
10 30 100 300 1000 3000 10000 30000
mask_standard 2.156872 1.850663 2.034149 2.166312 2.164541 3.090372 2.981326 3.131151
mask_standard_loc 1.879035 1.782366 1.988823 2.338112 2.361391 3.036131 2.998112 2.990103
mask_with_values 1.010166 1.000000 1.005113 1.026363 1.028698 1.293741 1.007824 1.016919
mask_with_values_loc 1.196843 1.300228 1.000000 1.000000 1.038989 1.219233 1.037020 1.000000
query 4.997304 4.765554 5.934096 4.500559 2.997924 2.397013 1.680447 1.398190
xs_label 4.124597 4.272363 5.596152 4.295331 4.676591 5.710680 6.032809 8.950255
mask_with_isin 1.674055 1.679935 1.847972 1.724183 1.345111 1.405231 1.253554 1.264760
mask_with_in1d 1.000000 1.083807 1.220493 1.101929 1.000000 1.000000 1.000000 1.144175
Ви помітите, що швидкий час, здається, ділиться між mask_with_values
таmask_with_in1d
res.T.plot(loglog=True)
Функції
def mask_standard(df):
mask = df['A'] == 'foo'
return df[mask]
def mask_standard_loc(df):
mask = df['A'] == 'foo'
return df.loc[mask]
def mask_with_values(df):
mask = df['A'].values == 'foo'
return df[mask]
def mask_with_values_loc(df):
mask = df['A'].values == 'foo'
return df.loc[mask]
def query(df):
return df.query('A == "foo"')
def xs_label(df):
return df.set_index('A', append=True, drop=False).xs('foo', level=-1)
def mask_with_isin(df):
mask = df['A'].isin(['foo'])
return df[mask]
def mask_with_in1d(df):
mask = np.in1d(df['A'].values, ['foo'])
return df[mask]
Тестування
res = pd.DataFrame(
index=[
'mask_standard', 'mask_standard_loc', 'mask_with_values', 'mask_with_values_loc',
'query', 'xs_label', 'mask_with_isin', 'mask_with_in1d'
],
columns=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
dtype=float
)
for j in res.columns:
d = pd.concat([df] * j, ignore_index=True)
for i in res.index:a
stmt = '{}(d)'.format(i)
setp = 'from __main__ import d, {}'.format(i)
res.at[i, j] = timeit(stmt, setp, number=50)
Спеціальні терміни
Дивлячись на особливий випадок, коли у нас є один не-об’єкт dtype
для всього кадру даних.
Код нижче
spec.div(spec.min())
10 30 100 300 1000 3000 10000 30000
mask_with_values 1.009030 1.000000 1.194276 1.000000 1.236892 1.095343 1.000000 1.000000
mask_with_in1d 1.104638 1.094524 1.156930 1.072094 1.000000 1.000000 1.040043 1.027100
reconstruct 1.000000 1.142838 1.000000 1.355440 1.650270 2.222181 2.294913 3.406735
Виявляється, реконструкція не варта цього декількох сотень рядів.
spec.T.plot(loglog=True)
Функції
np.random.seed([3,1415])
d1 = pd.DataFrame(np.random.randint(10, size=(10, 5)), columns=list('ABCDE'))
def mask_with_values(df):
mask = df['A'].values == 'foo'
return df[mask]
def mask_with_in1d(df):
mask = np.in1d(df['A'].values, ['foo'])
return df[mask]
def reconstruct(df):
v = df.values
mask = np.in1d(df['A'].values, ['foo'])
return pd.DataFrame(v[mask], df.index[mask], df.columns)
spec = pd.DataFrame(
index=['mask_with_values', 'mask_with_in1d', 'reconstruct'],
columns=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
dtype=float
)
Тестування
for j in spec.columns:
d = pd.concat([df] * j, ignore_index=True)
for i in spec.index:
stmt = '{}(d)'.format(i)
setp = 'from __main__ import d, {}'.format(i)
spec.at[i, j] = timeit(stmt, setp, number=50)
.iloc(numpy.where(..))
порівняти в цій схемі? ii) Чи очікуєте ви, що рейтинг буде однаковим при використанні декількох умов?
pd.Series.isin
, зверніть увагу , що робить використання np.in1d
під капотом в конкретному сценарії використання Хаш в інших, і неявно застосовується компроміс між вартістю хеширования по порівнянні з продуктивністю в конкретних ситуаціях. У цій відповіді детальніше.
[{P|EXP}TIME]
- і [{C|P|EXP}SPACE]
- витрати використання вище запропонованих форм блок-синтаксису (обробка зверху вниз за все dataframes відразу) ростуть , а саме , коли масштабується до деяких ~1E6, ~1E9, ~1E12
підрахунків рядків? Дякуємо, що показали нам всю картину, сер. Кількісні показання еталону з [min, Avg, MAX, StDev]
завжди вітаються, оскільки min
і MAX
значення, і значення супроводжують Mean/StDev
рельєф партії.
Панди, еквівалентні
select * from table where column_name = some_value
є
table[table.column_name == some_value]
Кілька умов:
table[(table.column_name == some_value) | (table.column_name2 == some_value2)]
або
table.query('column_name == some_value | column_name2 == some_value2')
import pandas as pd
# Create data set
d = {'foo':[100, 111, 222],
'bar':[333, 444, 555]}
df = pd.DataFrame(d)
# Full dataframe:
df
# Shows:
# bar foo
# 0 333 100
# 1 444 111
# 2 555 222
# Output only the row(s) in df where foo is 222:
df[df.foo == 222]
# Shows:
# bar foo
# 2 555 222
У наведеному вище коді це рядок, df[df.foo == 222]
який дає рядки на основі значення стовпця, 222
в цьому випадку.
Також можливі кілька умов:
df[(df.foo == 222) | (df.bar == 444)]
# bar foo
# 1 444 111
# 2 555 222
Але в цей момент я рекомендую використовувати функцію запиту , оскільки вона є менш багатослівною і дає такий же результат:
df.query('foo == 222 | bar == 444')
query
тут єдина відповідь, яка сумісна з ланцюжком методів. Схоже, це аналог панди filter
у dplyr.
[
не круглі дужки (
зовні.
|
це для AND, але, звичайно, це АБО-оператор ...
df[condition1][condition2]
df.query('`my col` == 124')
Я вважаю, що синтаксис попередніх відповідей є зайвим і важко запам'ятовується. Pandas ввів query()
метод у v0.13, і я його більше віддаю перевагу. Що стосується вашого питання, ви могли б зробитиdf.query('col == val')
Відтворено з http://pandas.pydata.org/pandas-docs/version/0.17.0/indexing.html#indexing-query
In [167]: n = 10
In [168]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [169]: df
Out[169]:
a b c
0 0.687704 0.582314 0.281645
1 0.250846 0.610021 0.420121
2 0.624328 0.401816 0.932146
3 0.011763 0.022921 0.244186
4 0.590198 0.325680 0.890392
5 0.598892 0.296424 0.007312
6 0.634625 0.803069 0.123872
7 0.924168 0.325076 0.303746
8 0.116822 0.364564 0.454607
9 0.986142 0.751953 0.561512
# pure python
In [170]: df[(df.a < df.b) & (df.b < df.c)]
Out[170]:
a b c
3 0.011763 0.022921 0.244186
8 0.116822 0.364564 0.454607
# query
In [171]: df.query('(a < b) & (b < c)')
Out[171]:
a b c
3 0.011763 0.022921 0.244186
8 0.116822 0.364564 0.454607
Ви також можете отримати доступ до змінних у середовищі, попередньо додавши до нього @
.
exclude = ('red', 'orange')
df.query('color not in @exclude')
numexpr
встановлений пакет .
.query
допомогою pandas >= 0.25.0
:Серпень 2019 оновлена відповідь
Оскільки pandas >= 0.25.0
ми можемо використовувати query
метод для фільтрації фреймів даних методами pandas і навіть іменами стовпців, які мають пробіли. Зазвичай пробіли у назвах стовпців дають помилку, але тепер ми можемо вирішити це за допомогою backtick (`) див. GitHub :
# Example dataframe
df = pd.DataFrame({'Sender email':['ex@example.com', "reply@shop.com", "buy@shop.com"]})
Sender email
0 ex@example.com
1 reply@shop.com
2 buy@shop.com
Використання .query
методу str.endswith
:
df.query('`Sender email`.str.endswith("@shop.com")')
Вихідні дані
Sender email
1 reply@shop.com
2 buy@shop.com
Також ми можемо використовувати локальні змінні, префіксуючи їх @
в запиті:
domain = 'shop.com'
df.query('`Sender email`.str.endswith(@domain)')
Вихідні дані
Sender email
1 reply@shop.com
2 buy@shop.com
Швидші результати можна досягти, використовуючи numpy.where .
Наприклад, із налаштуванням unubtu -
In [76]: df.iloc[np.where(df.A.values=='foo')]
Out[76]:
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Порівняння часу:
In [68]: %timeit df.iloc[np.where(df.A.values=='foo')] # fastest
1000 loops, best of 3: 380 µs per loop
In [69]: %timeit df.loc[df['A'] == 'foo']
1000 loops, best of 3: 745 µs per loop
In [71]: %timeit df.loc[df['A'].isin(['foo'])]
1000 loops, best of 3: 562 µs per loop
In [72]: %timeit df[df.A=='foo']
1000 loops, best of 3: 796 µs per loop
In [74]: %timeit df.query('(A=="foo")') # slowest
1000 loops, best of 3: 1.71 ms per loop
Ось простий приклад
from pandas import DataFrame
# Create data set
d = {'Revenue':[100,111,222],
'Cost':[333,444,555]}
df = DataFrame(d)
# mask = Return True when the value in column "Revenue" is equal to 111
mask = df['Revenue'] == 111
print mask
# Result:
# 0 False
# 1 True
# 2 False
# Name: Revenue, dtype: bool
# Select * FROM df WHERE Revenue = 111
df[mask]
# Result:
# Cost Revenue
# 1 444 111
Для вибору лише певних стовпців із кількох стовпців для заданого значення в пандах:
select col_name1, col_name2 from table where column_name = some_value.
Параметри:
df.loc[df['column_name'] == some_value][[col_name1, col_name2]]
або
df.query['column_name' == 'some_value'][[col_name1, col_name2]]
Щоб додати це відоме запитання (хоч і трохи пізно): Ви також df.groupby('column_name').get_group('column_desired_value').reset_index()
можете зробити новий кадр даних із вказаним стовпцем, який має конкретне значення. Напр
import pandas as pd
df = pd.DataFrame({'A': 'foo bar foo bar foo bar foo foo'.split(),
'B': 'one one two three two two one three'.split()})
print("Original dataframe:")
print(df)
b_is_two_dataframe = pd.DataFrame(df.groupby('B').get_group('two').reset_index()).drop('index', axis = 1)
#NOTE: the final drop is to remove the extra index column returned by groupby object
print('Sub dataframe where B is two:')
print(b_is_two_dataframe)
Виконати це дає:
Original dataframe:
A B
0 foo one
1 bar one
2 foo two
3 bar three
4 foo two
5 bar two
6 foo one
7 foo three
Sub dataframe where B is two:
A B
0 foo two
1 foo two
2 bar two
get_group()
автоматично поверне фрейм даних. Також ви можете просто сказати "drop = True" як параметр reset_index()
. Іншими словами, його можна скоротити до: b_is_two_dataframe = df.groupby('B').get_group('two').reset_index(drop=True)
Ви також можете використовувати .apply:
df.apply(lambda row: row[df['B'].isin(['one','three'])])
Він фактично працює в рядковому порядку (тобто застосовує функцію до кожного рядка).
Вихід є
A B C D
0 foo one 0 0
1 bar one 1 2
3 bar three 3 6
6 foo one 6 12
7 foo three 7 14
Результати такі ж, як і використання, як згадувало @unutbu
df[[df['B'].isin(['one','three'])]]