Как выбрать строки на 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)]
Обратите внимание на круглые скобки. Благодаря Питон правила приоритета операций , &
связывает более плотно , чем <=
и >=
. Таким образом, скобки в последнем примере необходимы. Без скобок
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
сюда добавлять ?
Есть несколько способов выбора строк во фрейме данных Pandas:
df[df['col'] == value
])df.iloc[...]
)df.xs(...)
)df.query(...)
APIНиже я покажу вам примеры каждого из них, с советами, когда использовать определенные методы. Предположим, наш критерий - столбец 'A'
=='foo'
(Примечание по производительности: для каждого базового типа мы можем упростить задачу, используя API pandas, или мы можем выйти за пределы API, как правило numpy
, и ускорить процесс.)
Настройка
Первое, что нам нужно, это определить условие, которое будет служить нашим критерием для выбора строк. Мы начнем со случая OP 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, но, конечно, это оператор OR ...
df[condition1][condition2]
df.query('`my col` == 124')
Я нахожу синтаксис предыдущих ответов излишним и трудным для запоминания. Панда представил 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 и даже имен столбцов, в которых есть пробелы. Обычно пробелы в именах столбцов приводят к ошибке, но теперь мы можем решить эту проблему с помощью обратного символа (`), см. 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'])]]