Выберите строки из DataFrame на основе значений в столбце в pandas

Как выбрать строки из DataFrame на основе значений в некотором столбце в пандах?

В SQL я бы использовал:

SELECT *
FROM table
WHERE colume_name = some_value

Я попытался просмотреть документацию панд, но не сразу нашел ответ.

Ответ 1

Чтобы выбрать строки, значение столбца которых равно скаляру, 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

Ответ 2

ТЛ; др

Панды, эквивалентные

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')

Ответ 3

Есть несколько способов выбора строк во фрейме данных Pandas:

  1. Булева индексация (df[df['col'] == value ])
  2. Позиционная индексация (df.iloc[...])
  3. df.xs(...) меток (df.xs(...))
  4. 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})

1. Булева индексация

... Булева индексация требует нахождения истинного значения каждого столбца строки '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.


2. Позиционная индексация

Позиционная индексация (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

3. Ярлык индексирования

Индексирование меток может быть очень удобным, но в этом случае мы снова делаем больше работы без пользы

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

4. API 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)

enter image description here

функции

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)

enter image description here

функции

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)

Ответ 4

Я считаю, что синтаксис предыдущих ответов является избыточным и трудно запоминаемым. 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')

Ответ 5

Более быстрые результаты могут быть достигнуты с помощью 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

Ответ 6

Вот простой пример

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

Ответ 7

Для выбора только определенных столбцов из нескольких столбцов для заданного значения в 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]]

Ответ 8

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

Для одного значения наиболее простым (читаемым человеком), вероятно, является:

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

Ответ 9

Чтобы добавить к этому известному вопросу (хотя и слишком поздно): вы также можете сделать 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

Ответ 10

Если вы находите строки на основе некоторого целого числа в столбце, то

df.loc[df['column_name'] == 2017]

Если вы находите значение на основе строки

df.loc[df['column_name'] == 'string']

Если на основе обоих

df.loc[(df['column_name'] == 'string') & (df['column_name'] == 2017)]

Ответ 11

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

Ответ 12

Если вы пришли сюда, чтобы выбрать строки из фреймворка данных, включив те, чье значение столбца НЕ является ни одним из списков значений, здесь, как перевернуть ответ 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

Ответ 13

Вы также можете использовать.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'])]]

Ответ 14

df.loc[df['column_name'] == some_value]