Как выбрать строки из DataFrame
на основе значений в некотором столбце в пандах?
В SQL я бы использовал:
SELECT *
FROM table
WHERE colume_name = some_value
Я попытался просмотреть документацию панд, но не сразу нашел ответ.
Как выбрать строки из DataFrame
на основе значений в некотором столбце в пандах?
В 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
, some_values
логический ряд с помощью ~
:
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
Панды, эквивалентные
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')
Есть несколько способов выбора строк во фрейме данных Pandas:
df[df['col'] == value
])df.iloc[...]
)df.xs(...)
меток (df.xs(...)
)df.query(...)
Ниже я покажу вам примеры каждого из них, с советами, когда использовать определенные методы. Предположим, нашим критерием является столбец 'A'
== 'foo'
(Примечание по производительности: для каждого базового типа мы можем упростить задачу, используя API pandas, или мы можем выйти за пределы API, обычно в numpy
, и ускорить процесс.)
Настроить
Первое, что нам нужно, это определить условие, которое будет служить нашим критерием для выбора строк. Мы начнем с OP case 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()
pd.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
от накладных расходов на создание другого 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
dtype
. Таким образом, требуется astype(df.dtypes)
и 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
особый случай, когда у нас есть один необъектный dtype
- 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)
Я считаю, что синтаксис предыдущих ответов является избыточным и трудно запоминаемым. 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')
Более быстрые результаты могут быть достигнуты с помощью 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
Для выбора только определенных столбцов из нескольких столбцов для заданного значения в pandas:
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.loc[df['column_name'] == some_value]
Для списков значений вы также можете использовать:
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
Обратите внимание, однако, что если вы хотите сделать это много раз, более эффективно сначала сделать индекс A, а затем использовать df.loc:
df = df.set_index(['A'])
print(df.loc['foo'])
дает
A B C D
foo one 0 0
foo two 2 4
foo two 4 8
foo one 6 12
foo three 7 14
Чтобы добавить к этому известному вопросу (хотя и слишком поздно): вы также можете сделать 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
Если вы находите строки на основе некоторого целого числа в столбце, то
df.loc[df['column_name'] == 2017]
Если вы находите значение на основе строки
df.loc[df['column_name'] == 'string']
Если на основе обоих
df.loc[(df['column_name'] == 'string') & (df['column_name'] == 2017)]
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})
df[df['A']=='foo']
OUTPUT:
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
Если вы пришли сюда, чтобы выбрать строки из фреймворка данных, включив те, чье значение столбца НЕ является ни одним из списков значений, здесь, как перевернуть ответ unutbu для списка значений выше:
df.loc[~df['column_name'].isin(some_values)]
(Чтобы не включать одно значение, конечно, вы просто используете обычный оператор не равно, !=
.)
Пример:
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(df)
дает нам
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
Подмножество только для тех строк, которые ARE NOT one
или three
в столбце B
:
df.loc[~df['B'].isin(['one', 'three'])]
дает
A B
2 foo two
4 foo two
5 bar two
Вы также можете использовать.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'])]]
df.loc[df['column_name'] == some_value]
Остерегайтесь цепной индексации. Это может вызвать ошибки: https://medium.com/dunder-data/selecting-subsets-of-data-in-pandas-part-4-c4216f84d388