Question Sélectionnez les lignes d'un DataFrame en fonction des valeurs d'une colonne dans pandas


Comment sélectionner des lignes d'un DataFrame basé sur des valeurs dans une colonne de pandas?
En SQL j'utiliserais:

select * from table where colume_name = some_value. 

J'ai essayé de regarder la documentation de pandas mais n'ai pas immédiatement trouvé la réponse.


821
2018-06-12 17:42


origine


Réponses:


Pour sélectionner des lignes dont la valeur de colonne est égale à un scalaire, some_value, utilisation ==:

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

Pour sélectionner des lignes dont la valeur de colonne est dans une itérable, some_values, utilisation isin:

df.loc[df['column_name'].isin(some_values)]

Combiner plusieurs conditions avec &:

df.loc[(df['column_name'] == some_value) & df['other_column'].isin(some_values)]

Pour sélectionner les lignes dont la valeur de colonne n'est pas égal  some_value, utilisation !=:

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

isin renvoie une série booléenne, afin de sélectionner des lignes dont la valeur est ne pas dans some_values, annuler la série booléenne en utilisant ~:

df.loc[~df['column_name'].isin(some_values)]

Par exemple,

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

rendements

     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

Si vous souhaitez inclure plusieurs valeurs, placez-les dans un liste (ou plus généralement, tout itérable) et utiliser isin:

print(df.loc[df['B'].isin(['one','three'])])

rendements

     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

Notez, cependant, que si vous souhaitez le faire plusieurs fois, il est plus efficace de faites d'abord un index, puis utilisez df.loc:

df = df.set_index(['B'])
print(df.loc['one'])

rendements

       A  C   D
B              
one  foo  0   0
one  bar  1   2
one  foo  6  12

ou, pour inclure plusieurs valeurs de l'utilisation de l'index df.index.isin:

df.loc[df.index.isin(['one','two'])]

rendements

       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

1646
2018-06-12 17:44



tl; dr

Les pandas équivalents à

select * from table where column_name = some_value

est

table[table.column_name == some_value]

Plusieurs conditions:

table[(table.column_name == some_value) | (table.column_name2 == some_value2)]

ou

table.query('column_name == some_value | column_name2 == some_value2')

Exemple de code

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

Dans le code ci-dessus c'est la ligne df[df.foo == 222] qui donne les lignes en fonction de la valeur de la colonne, 222 dans ce cas.

Plusieurs conditions sont également possibles:

df[(df.foo == 222) | (df.bar == 444)]
#    bar  foo
# 1  444  111
# 2  555  222

Mais à ce stade, je recommanderais d'utiliser le question fonction, car il est moins verbeux et donne le même résultat:

df.query('foo == 222 | bar == 444')

134
2017-07-08 15:17



Il existe quelques façons de sélectionner des lignes à partir d'une base de données pandas.

  1. L'indexation booléenne
  2. Indexation positionnelle
  3. Indexation d'étiquettes
  4. API

Pour chaque type de base, nous pouvons garder les choses simples en nous limitant à l'API pandas ou nous pouvons nous aventurer en dehors de l'API, généralement en numpyet accélérer les choses.

Je vais vous montrer des exemples de chacun et vous guider pour savoir quand utiliser certaines techniques.


Installer
La première chose dont nous aurons besoin est d'identifier une condition qui servira de critère pour sélectionner les lignes. L'OP offre jusqu'à column_name == some_value. Nous allons commencer là et inclure d'autres cas d'utilisation courants.

Emprunt de @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})

Supposons que notre critère est la colonne 'A' = 'foo'

1.
Booléen l'indexation nécessite de trouver la valeur de vérité de chaque rangée 'A' colonne étant égale à 'foo', puis en utilisant ces valeurs de vérité pour identifier les lignes à conserver. Typiquement, nous nommerions cette série, un tableau de valeurs de vérité, mask. Nous le ferons aussi ici.

mask = df['A'] == 'foo'

Nous pouvons ensuite utiliser ce masque pour découper ou indexer le dataframe

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

C'est l'un des moyens les plus simples pour accomplir cette tâche et si la performance ou l'intuitivité ne sont pas un problème, cela devrait être votre méthode choisie. Cependant, si la performance est une préoccupation, alors vous pourriez envisager une autre façon de créer le mask.


2.
Positionnel l'indexation a ses cas d'utilisation, mais ce n'est pas l'un d'entre eux. Afin d'identifier où découper, nous devons d'abord effectuer la même analyse booléenne que nous avons faite ci-dessus. Cela nous laisse effectuer une étape supplémentaire pour accomplir la même tâche.

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.
Étiquette l'indexation peut être très pratique, mais dans ce cas, nous faisons encore plus de travail sans bénéfice

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.
pd.DataFrame.query est un moyen très élégant / intuitif pour effectuer cette tâche. Mais est souvent plus lent. toutefois, si vous faites attention aux horaires ci-dessous, pour les données volumineuses, la requête est très efficace. Plus que l'approche standard et d'une ampleur similaire à ma meilleure suggestion.

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

Ma préférence est d'utiliser le Boolean  mask 

Des améliorations réelles peuvent être faites en modifiant la façon dont nous créons notre Boolean  mask.

mask alternative 1
Utilisez le sous-jacent numpy tableau et renoncer à la surcharge de créer un autre pd.Series 

mask = df['A'].values == 'foo'

Je vais montrer des tests de temps plus complets à la fin, mais jetez juste un coup d'œil aux gains de performance que nous obtenons en utilisant l'échantillon de données. D'abord, nous regardons la différence dans la création de la 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)

Évaluer le mask avec le numpy le tableau est ~ 30 fois plus rapide. Ceci est en partie dû à numpy l'évaluation étant souvent plus rapide. C'est aussi en partie dû au manque de frais généraux nécessaires pour construire un index et un pd.Series objet.

Ensuite, nous allons regarder le moment de trancher avec un mask par rapport à l'autre.

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)

Les gains de performance ne sont pas aussi prononcés. Nous verrons si cela résiste à des tests plus robustes.


mask alternative 2
Nous aurions pu reconstruire le dataframe aussi. Il y a une grande mise en garde lors de la reconstruction d'un dataframe - vous devez prendre soin de dtypes en faisant ça!

Au lieu de df[mask] nous le ferons

pd.DataFrame(df.values[mask], df.index[mask], df.columns).astype(df.dtypes)

Si la base de données est de type mixte, ce qui est notre exemple, alors quand nous obtenons df.values le tableau résultant est de dtype  object et par conséquent, toutes les colonnes de la nouvelle base de données seront de dtype  object. Exigeant ainsi astype(df.dtypes) et tuer tous les gains de performance potentiels.

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

Cependant, si la base de données n'est pas de type mixte, c'est un moyen très utile de le faire.

Donné

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)

Contre

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

Nous avons réduit le temps de moitié.


mask alternative 3
@unutbu nous montre également comment utiliser pd.Series.isin rendre compte de chaque élément de df['A'] être dans un ensemble de valeurs. Cela évalue à la même chose si notre ensemble de valeurs est un ensemble d'une valeur, à savoir 'foo'. Mais il généralise aussi pour inclure de plus grands ensembles de valeurs si nécessaire. Il s'avère que c'est encore assez rapide, même si c'est une solution plus générale. La seule vraie perte est dans l'intuitivité pour ceux qui ne connaissent pas le concept.

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

Cependant, comme avant, nous pouvons utiliser numpy pour améliorer les performances tout en ne sacrifiant pratiquement rien. Nous utiliserons 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

Timing
Je vais inclure d'autres concepts mentionnés dans d'autres messages ainsi pour référence.
Code ci-dessous 

Chaque colonne de ce tableau représente une longueur de données différente sur laquelle nous testons chaque fonction. Chaque colonne montre le temps relatif pris, avec la fonction la plus rapide étant donné un indice de base de 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

Vous remarquerez que les meilleurs temps semblent être partagés entre mask_with_values et mask_with_in1d

res.T.plot(loglog=True)

enter image description here

Les fonctions 

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]

Essai 

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)

Timing spécial
En regardant le cas particulier quand nous avons un seul non-objet dtypepour l'ensemble de la base de données. Code ci-dessous 

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

Il s'avère que la reconstruction ne vaut pas le coup après quelques centaines de lignes.

spec.T.plot(loglog=True)

enter image description here

Les fonctions 

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
)

Essai 

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)

76
2017-09-11 22:14



Je trouve la syntaxe des réponses précédentes redondante et difficile à retenir. Pandas a présenté le query() méthode en v0.13 et je préfère beaucoup. Pour ta question, tu pourrais faire df.query('col == val')

Reproduit de 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

Vous pouvez également accéder aux variables dans l'environnement en ajoutant @.

exclude = ('red', 'orange')
df.query('color not in @exclude')

41
2018-02-09 01:36



Des résultats plus rapides peuvent être obtenus en utilisant numpy.where.

Par exemple, avec l'installation d'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

Comparaisons de temps:

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

11
2017-07-05 16:34



Voici un exemple simple

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

10
2018-06-13 11:49



J'ai juste essayé d'éditer ceci, mais je n'étais pas connecté, ainsi je ne suis pas sûr où mon édition est allée. J'essayais d'incorporer la sélection multiple. Donc je pense qu'une meilleure réponse est:

Pour une seule valeur, la plus simple (lisible par l'homme) est probablement:

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

Pour les listes de valeurs, vous pouvez également utiliser:

df.loc[df['column_name'].isin(some_values)]

Par exemple,

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

rendements

     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

Si vous souhaitez sélectionner plusieurs critères, vous pouvez les placer dans une liste et utiliser 'isin':

print(df.loc[df['B'].isin(['one','three'])])

rendements

      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

Notez cependant que si vous souhaitez le faire plusieurs fois, il est plus efficace de commencer par l'index A, puis d'utiliser df.loc:

df = df.set_index(['A'])
print(df.loc['foo'])

rendements

  A      B  C   D
foo    one  0   0
foo    two  2   4
foo    two  4   8
foo    one  6  12
foo  three  7  14

7
2018-01-25 23:27



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

3
2018-03-06 06:02



Pour ajouter à cette fameuse question (mais un peu trop tard): Vous pouvez aussi faire df.groupby('column_name').get_group('column_desired_value').reset_index() pour créer une nouvelle trame de données avec une colonne spécifiée ayant une valeur particulière. Par exemple.

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)

Exécutez ceci donne:

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

3
2017-11-18 12:10