J'ai un dataframe du type suivant-
df
colnames = df.columns.tolist()[:-1] list_name=[] for i,c in enumerate(colnames): if i!=len(colnames): for k in range(i+1,len(colnames)): df[c+'_'+colnames[k]]=(df[c]- df[colnames[k]])/(df[c]+df[colnames[k]]) list_name.append(c+'_'+colnames[k])
Je veux que l'opération suivante soit effectuée -
A_B: A-B/A+B A_C: A-C/A+C B_C: B-C/B+C
A_B, A_C, B_C correspond à -
p >
A_B A_C B_C -0.33 -0.5 -0.2 -0.11 -0.2 -0.09
ce que je fais en utilisant-
A B C 5 10 15 20 25 30
Mais le problème est que mon dataframe réel est de la taille de 5 * 381
forme de sorte que le nombre réel de combinaisons de A_B, A_C et ainsi de suite
se présente sous la forme 5 * 72390
, ce qui prend 60 minutes à s'exécuter.
J'essaie donc de le convertir en tableau numpy afin de pouvoir l'optimiser à l'aide de Numba pour le calculer efficacement ( Approche de programmation parallèle pour résoudre les problèmes de pandas ) mais je suis incapable de le convertir en tableau numpy.
En outre, toute autre solution pour résoudre ce problème est également la bienvenue.
3 Réponses :
Pandas a une fonction intégrée pour ce faire: df.values
def A_B(x): return (x[0]-x[1])/(x[0]+x[1]) def A_C(x): return (x[0]-x[2])/(x[0]+x[2]) def B_C(x): return (x[1]-x[2])/(x[1]+x[2]) def combine(x): return pd.DataFrame({'A_B': A_B(x), 'A_C': A_C(x), 'B_C': B_C(x)}) combine(df.values.T) # A_B A_C B_C # 0 -0.333333 -0.5 -0.200000 # 1 -0.111111 -0.2 -0.090909
Et le calcul ultérieur de A_B, A_C et B_C. p >
import pandas as pd df = pd.DataFrame({'A': [5, 20], 'B': [10, 25], 'C': [15,30]}) print(df.head()) # A B C # 0 5 10 15 # 1 20 25 30 print(df.values) # array([[ 5, 10, 15], # [20, 25, 30]], dtype=int64)
Oui, mais comment effectuer A-B / A + B
et des étapes similaires pour le tableau obtenu.
@Bing, vous avez demandé comment le convertir en np.ndarray
. Les gens sont encouragés à résoudre leurs problèmes par eux-mêmes. Nous sommes ici pour aider et orienter les gens dans la bonne direction, pas pour faire s.o. travail.
Oui, mais le problème réel était l'optimisation, pas la conversion si vous l'aviez observé.
Utilisation :
In [4]: %%timeit ...: a, b = zip(*(combinations(np.arange(len(df.columns)), 2))) ...: arr = df.values ...: cols = df.columns.values ...: arr1 = arr[:, a] ...: arr2 = arr[:, b] ...: c = [f'{x}_{y}' for x, y in zip(cols[np.array(a)], cols[np.array(b)])] ...: pd.DataFrame((arr1-arr2)/(arr1+arr2), columns=c) ...: 62 ms ± 7.29 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) In [5]: %%timeit ...: a, b = zip(*(combinations(df.columns, 2))) ...: df1 = df.loc[:, a] ...: df2 = df.loc[:, b] ...: arr1 = df1.values ...: arr2 = df2.values ...: c = [f'{x}_{y}' for x, y in zip(a, b)] ...: pd.DataFrame((arr1-arr2)/(arr1+arr2), columns=c) ...: 63.2 ms ± 253 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) In [7]: %%timeit ...: func1(df) ...: 89.2 ms ± 331 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) In [8]: %%timeit ...: a, b = zip(*(combinations(df.columns, 2))) ...: df1 = df.loc[:, a] ...: df2 = df.loc[:, b] ...: c = [f'{x}_{y}' for x, y in zip(a, b)] ...: pd.DataFrame((df1.values-df2.values)/(df1.values+df2.values), columns=c) ...: 69.8 ms ± 6.04 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Commencez par obtenir toutes les combinaisons de colonnes dans 2 listes ( a
est pour la première valeur des tuples, b
est pour la seconde):
np.random.seed(2019) df = pd.DataFrame(np.random.randint(10,100,(5,381))) df.columns = ['c'+str(i+1) for i in range(df.shape[1])] #print (df)
Ensuite, utilisez DataFrame.loc
pour répéter les colonnes par listes:
from itertools import combinations a, b = zip(*(combinations(np.arange(len(df.columns)), 2))) arr = df.values cols = df.columns.values arr1 = arr[:, a] arr2 = arr[:, b] c = [f'{x}_{y}' for x, y in zip(cols[np.array(a)], cols[np.array(b)])] df = pd.DataFrame((arr1-arr2)/(arr1+arr2), columns=c)
Convertir les valeurs en numpy tableaux pour DataFrame final et obtenir de nouveaux noms de colonnes par compréhension de la liste:
c = [f'{x}_{y}' for x, y in zip(a, b)] arr1 = df1.values arr2 = df2.values df = pd.DataFrame((arr1-arr2)/(arr1+arr2), columns=c) print (df) A_B A_C B_C 0 -0.333333 -0.5 -0.200000 1 -0.111111 -0.2 -0.090909
Une autre solution est très similaire, ne créez une combinaison que par arange
par longueur de colonnes et les derniers nouveaux noms de colonnes sont créés par indexation:
df1 = df.loc[:, a] print (df1) A A B 0 5 5 10 1 20 20 25 df2 = df.loc[:, b] print (df2) B C C 0 10 15 15 1 25 30 30
Performance :
Testé sur 5 lignes et 381 colonnes:
from itertools import combinations a, b = zip(*(combinations(df.columns, 2)))
df = pd.DataFrame({ 'A':[5,20], 'B':[10,25], 'C':[15,30] }) print (df) A B C 0 5 10 15 1 20 25 30
C'est du génie, c'est vraiment du génie.
@Bing - Hmmm, ça dépend. btw, est-il possible de changer le titre de la question quelque chose comme Augmenter les performances des opérations arithmétiques avec des combinaisons de noms de colonnes
?
En voici un utilisant NumPy et sa puissante fonctionnalité de slicing
-
In [147]: df = cdf(np.random.randint(10,100,(5,381))) ...: df.columns = ['c'+str(i+1) for i in range(df.shape[1])] # @jezrael's soln In [148]: %%timeit ...: a, b = zip(*(combinations(df.columns, 2))) ...: df1 = df.loc[:, a] ...: df2 = df.loc[:, b] ...: c = [x+'_'+y for x, y in zip(a, b)] ...: pd.DataFrame((df1.values-df2.values)/(df1.values+df2.values), columns=c) 10 loops, best of 3: 58.1 ms per loop # From this post In [149]: %timeit func1(df) 10 loops, best of 3: 22.6 ms per loop
Exemple d'exécution -
In [361]: df Out[361]: A B C 0 5 10 15 1 20 25 30 In [362]: func1(df) Out[362]: A_B A_C B_C 0 -0.333333 -0.5 -0.200000 1 -0.111111 -0.2 -0.090909
Timings sur 5 x 381
tableau aléatoire -
def func1(df): a = df.values n = a.shape[1] L = n*(n-1)//2 idx = np.concatenate(( [0], np.arange(n-1,0,-1).cumsum() )) start, stop = idx[:-1], idx[1:] c = df.columns.values.astype(str) d = 2*int(''.join(x for x in str(c.dtype) if x.isdigit()))+1 outc = np.empty(L,dtype='S'+str(2*d+1)) out = np.empty((a.shape[0],L)) for i,(s0,s1) in enumerate(zip(start, stop)): outc[s0:s1] = np.char.add(c[i]+'_',c[i+1:]) out[:,s0:s1] = (a[:,i,None]-a[:,i+1:])/(a[:,i,None]+a[:,i+1:]) return pd.DataFrame(out,columns=outc)
Je n'ai pas encore testé cette approche, je la mettrai à jour après l'avoir testée.
@Bing a un peu modifié mon approche pour améliorer les performances. En outre, les horaires ajoutés, espérons qu'ils seront comparables aux résultats de votre scénario de test.
Modifiez simplement la réponse pour ne pas répéter l'appel .values
, est-il possible d'ajouter aux horaires? Je vous remercie.
@jezrael Ne semble pas beaucoup changer les horaires, en fait semble légèrement plus lent avec les nouveaux changements. Essayez-le à votre fin?
@Divakar - Cela semble aussi dépendre des timings par nombre de lignes, testé avec 1000 lignes et un peu plus rapide (sous python 3.6, pandas 0.24.1, win7)
@Divakar - et j'oublie - a ajouté des horaires à ma réponse.
Est-il important d'obtenir les bons noms de colonne pour la sortie?
@Divakar Oui, c'est important.
Avez-vous pu tester les approches affichées de votre côté?
@Divakar Oui, cela a parfaitement fonctionné, merci