web-dev-qa-db-fra.com

Pandas DataFrame vérifie si la valeur de colonne existe dans un groupe de colonnes

J'ai un DataFrame comme celui-ci (exemple simplifié)

id  v0  v1  v2  v3  v4
1   10  5   10  22  50
2   22  23  55  60  50
3   8   2   40  80  110
4   15  15  25  100 101

Et je voudrais créer une colonne supplémentaire qui soit 1 ou 0. 1 si la valeur v0 est dans les valeurs de v1 à v4, et 0 si ce n'est pas le cas. Ainsi, dans cet exemple pour id 1, la valeur doit être 1 (puisque v2 = 10) et pour id 2, la valeur doit être 0 car 22 n'est pas dans v1 à v4.

En réalité, le tableau est beaucoup plus grand (environ 100 000 lignes et variables vont de v1 à v99).

14
EGM8686

Vous pouvez utiliser les tableaux numpy sous-jacents pour les performances:

Configuration

a = df.v0.values
b = df.iloc[:, 2:].values

df.assign(out=(a[:, None]==b).any(1).astype(int))
   id  v0  v1  v2   v3   v4  out
0   1  10   5  10   22   50    1
1   2  22  23  55   60   50    0
2   3   8   2  40   80  110    0
3   4  15  15  25  100  101    1

Cette solution exploite la diffusion pour permettre une comparaison par paire:

Tout d'abord, nous diffusons a:

>>> a[:, None]
array([[10],
       [22],
       [ 8],
       [15]], dtype=int64)

Ce qui permet une comparaison par paire avec b:

>>> a[:, None] == b
array([[False,  True, False, False],
       [False, False, False, False],
       [False, False, False, False],
       [ True, False, False, False]])

Nous vérifions ensuite simplement les résultats True le long du premier axe et les convertissons en entier.


Performance


Fonctions

def user_chris(df):
    a = df.v0.values
    b = df.iloc[:, 2:].values
    return (a[:, None]==b).any(1).astype(int)

def rahlf23(df):
    df = df.set_index('id')
    return df.drop('v0', 1).isin(df['v0']).any(1).astype(int)

def chris_a(df):
    return df.loc[:, "v1":].eq(df['v0'], 0).any(1).astype(int)

def chris(df):
    return df.apply(lambda x: int(x['v0'] in x.values[2:]), axis=1)

def anton_vbr(df):
    df.set_index('id', inplace=True)
    return df.isin(df.pop('v0')).any(1).astype(int)

Configuration

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from timeit import timeit

res = pd.DataFrame(
       index=['user_chris', 'rahlf23', 'chris_a', 'chris', 'anton_vbr'],
       columns=[10, 50, 100, 500, 1000, 5000],
       dtype=float
)

for f in res.index:
    for c in res.columns:
        vals = np.random.randint(1, 100, (c, c))
        vals = np.column_stack((np.arange(vals.shape[0]), vals))
        df = pd.DataFrame(vals, columns=['id'] + [f'v{i}' for i in range(0, vals.shape[0])])
        stmt = '{}(df)'.format(f)
        setp = 'from __main__ import df, {}'.format(f)
        res.at[f, c] = timeit(stmt, setp, number=50)

ax = res.div(res.min()).T.plot(loglog=True)
ax.set_xlabel("N");
ax.set_ylabel("time (relative)");

plt.show()

Sortie

enter image description here

12
user3483203

Que diriez-vous:

df['new_col'] = df.loc[:, "v1":].eq(df['v0'],0).any(1).astype(int)

[en dehors]

   id  v0  v1  v2   v3   v4  new_col
0   1  10   5  10   22   50        1
1   2  22  23  55   60   50        0
2   3   8   2  40   80  110        0
3   4  15  15  25  100  101        1
3
Chris A

Je suppose ici que id est défini comme votre index de trame de données ici:

df = df.set_index('id')

Ensuite, les éléments suivants devraient fonctionner (réponse similaire ici ):

df['New'] = df.drop('v0', 1).isin(df['v0']).any(1).astype(int)

Donne:

    v0  v1  v2   v3   v4  New
id                           
1   10   5  10   22   50    1
2   22  23  55   60   50    0
3    8   2  40   80  110    0
4   15  15  25  100  101    1
2
rahlf23

Vous pouvez également utiliser une fonction lambda:

df['newCol'] = df.apply(lambda x: int(x['v0'] in x.values[2:]), axis=1)

    id  v0  v1  v2  v3  v4  newCol
0   1   10  5   10  22  50  1
1   2   22  23  55  60  50  0
2   3   8   2   40  80  110 0
3   4   15  15  25  100 101 1
2
Chris

Une autre prise, probablement la plus petite syntaxe:

df['new'] = df.isin(df.pop('v0')).any(1).astype(int)

Preuve complète:

import pandas as pd

data = '''\
id  v0  v1  v2  v3  v4
1   10  5   10  22  50
2   22  23  55  60  50
3   8   2   40  80  110
4   15  15  25  100 101'''

df = pd.read_csv(pd.compat.StringIO(data), sep='\s+')
df.set_index('id', inplace=True)
df['new'] = df.isin(df.pop('v0')).any(1).astype(int)
print(df)

Retour:

    v1  v2   v3   v4  new
id                       
1    5  10   22   50    1
2   23  55   60   50    0
3    2  40   80  110    0
4   15  25  100  101    1
2
Anton vBR