J'ai un dataframe de pandas df de la forme:

     Col1      Col2      Col3      Col4

0    True      False     True      False
1    False     False     False     False
2    False     True      False     False
3    True      True      True      True

Ici, True et False sont des valeurs booléennes.

J'essaye de générer un nouveau dataframe pandas new_df qui devrait ressembler à:

     Matched_Cols

0    [Col1, Col3]
1    []
2    [Col2]
3    [Col1, Col2, Col3, Col4]

Quelle est la manière la plus efficace d'y parvenir?

1
Melsauce 1 juin 2020 à 10:33

4 réponses

Meilleure réponse

Approche n ° 1

Voici avec le traitement des données de tableau -

def iter_accum(df):
    c = df.columns.values.astype(str)
    return pd.DataFrame({'Matched_Cols':[c[i] for i in df.values]})

Exemple de sortie -

In [41]: df
Out[41]: 
    Col1   Col2   Col3   Col4
0   True  False   True  False
1  False  False  False  False
2  False   True  False  False
3   True   True   True   True

In [42]: iter_accum(df)
Out[42]: 
               Matched_Cols
0              [Col1, Col3]
1                        []
2                    [Col2]
3  [Col1, Col2, Col3, Col4]

Approche n ° 2

Un autre avec découpage sur les données de tableau et une indexation booléenne -

def slice_accum(df):
    c = df.columns.values.astype(str)
    a = df.values
    vals = np.broadcast_to(c,a.shape)[a]
    I = np.r_[0,a.sum(1).cumsum()]
    ac = []
    for (i,j) in zip(I[:-1],I[1:]):
        ac.append(vals[i:j])
    return pd.DataFrame({'Matched_Cols':ac})

Analyse comparative

Autre (s) solution (s) proposée (s) -

# @jezrael's soln-1
def jez1(df):
    return df.apply(lambda x: x.index[x].tolist(), axis=1)

# @jezrael's soln-2
def jez2(df):
    return df.dot(df.columns + ',').str.rstrip(',').str.split(',')

# @Shubham Sharma's soln
def Shubham1(df):
    return df.agg(lambda s: s.index[s].values, axis=1) 

# @sammywemmy's soln
def sammywemmy1(df):
    return pd.DataFrame({'Matched_Cols':[np.compress(x,y) for x,y in zip(df.to_numpy(),np.tile(df.columns,(len(df),1)))]})

Utilisation du package benchit (quelques outils d'analyse comparative regroupés; avertissement: je suis son auteur) pour comparer les solutions proposées.

import benchit
funcs = [iter_accum,slice_accum,jez1,jez2,Shubham1,sammywemmy1]
in_ = {n:pd.DataFrame(np.random.rand(n,n)>0.5, columns=['Col'+str(i) for i in range(1,n+1)]) for n in [4,20,100,200,500,1000]}
t = benchit.timings(funcs, in_, input_name='Len')
t.rank()
t.plot(logx=True)

enter image description here

3
Divakar 1 juin 2020 à 08:23

Inutilement long:

df['Matched_Col'] = [np.compress(x,y) for x,y in zip(df.to_numpy(),np.tile(df.columns,(len(df),1)))]

Col1    Col2    Col3    Col4    Matched_Col
0   True    False   True    False   [Col1, Col3]
1   False   False   False   False   []
2   False   True    False   False   [Col2]
3   True    True    True    True    [Col1, Col2, Col3, Col4]
1
sammywemmy 1 juin 2020 à 07:47

Utilisation:

df['Matched_Cols'] = df.agg(lambda s: s.index[s].values, axis=1) 

Résultat:

   Col1   Col2   Col3   Col4               Matched_Cols

0   True  False   True  False              [Col1, Col3]
1  False  False  False  False                        []
2  False   True  False  False                    [Col2]
3   True   True   True   True  [Col1, Col2, Col3, Col4]
1
Shubham Sharma 1 juin 2020 à 08:02

Vous pouvez filtrer pour chaque valeur d'index de ligne, quels sont les noms de colonnes dans l'original DataFrame, puis les convertir en listes:

df['Matched_Cols'] = df.apply(lambda x: x.index[x].tolist(), axis=1)

Ou utilisez DataFrame.dot pour la multiplication de matrice avec des noms de colonnes avec séparateur, suppression de la dernière valeur de séparateur par Series.str.rstrip et dernière utilisation Series.str.split:

df['Matched_Cols'] = df.dot(df.columns + ',').str.rstrip(',').str.split(',')

print (df)
    Col1   Col2   Col3   Col4              Matched_Cols
0   True  False   True  False              [Col1, Col3]
1  False  False  False  False                        []
2  False   True  False  False                    [Col2]
3   True   True   True   True  [Col1, Col2, Col3, Col4]
2
jezrael 1 juin 2020 à 07:41