C'est un peu l'inverse de ce que la plupart des gens aimeraient faire lors de la conversion entre des listes et des dataframes.
Je cherche à convertir un grand dataframe (10M + lignes, 20 + colonnes) en une liste de chaînes, où chaque entrée est une représentation sous forme de chaîne de chaque ligne de la trame de données. Je peux le faire en utilisant la méthode to_csv () de pandas, mais je me demande s'il existe un moyen plus rapide car cela s'avère être un goulot d'étranglement dans mon code.
Minimum exemple de travail:
In [1]: df.head(10)
Out [1]: a b c d e
0 a_0 b_0 c_0 d_0 e_0
1 a_1 b_1 c_1 d_1 e_1
2 a_2 b_2 c_2 d_2 e_2
3 a_3 b_3 c_3 d_3 e_3
4 a_4 b_4 c_4 d_4 e_4
5 a_5 b_5 c_5 d_5 e_5
6 a_6 b_6 c_6 d_6 e_6
7 a_7 b_7 c_7 d_7 e_7
8 a_8 b_8 c_8 d_8 e_8
9 a_9 b_9 c_9 d_9 e_9
In [2]: ret_val[:10]
Out [2]: ['a_0,b_0,c_0,d_0,e_0',
'a_1,b_1,c_1,d_1,e_1',
'a_2,b_2,c_2,d_2,e_2',
'a_3,b_3,c_3,d_3,e_3',
'a_4,b_4,c_4,d_4,e_4',
'a_5,b_5,c_5,d_5,e_5',
'a_6,b_6,c_6,d_6,e_6',
'a_7,b_7,c_7,d_7,e_7',
'a_8,b_8,c_8,d_8,e_8',
'a_9,b_9,c_9,d_9,e_9']
L'aspect de conversion du code ci-dessus prend environ 90 secondes pour une trame de données de 10 000 000 lignes sur un seul thread de mon Core i9, et dépend fortement du processeur . J'adorerais réduire cela d'un ordre de grandeur si possible.
MODIFIER: Je ne cherche pas à enregistrer les données dans un fichier .csv ou dans un fichier. Je cherche juste à convertir le dataframe en un tableau de chaînes.
EDIT: Exemple d'entrée / sortie avec seulement 5 colonnes: p >
import numpy as np
import pandas as pd
# Create the initial dataframe.
size = 10000000
cols = list('abcdefghijklmnopqrstuvwxyz')
df = pd.DataFrame()
for col in cols:
df[col] = np.arange(size)
df[col] = "%s_" % col + df[col].astype(str)
# Convert to the required list structure
ret_val = _df_.to_csv(index=False, header=False).split("\n")[:-1]
4 Réponses :
Vous pouvez essayer différentes méthodes pour accélérer l'écriture des données sur le disque:
L'écriture d'un fichier compressé peut accélérer l'écriture jusqu'à 10x
df.to_csv ('output.csv.gz'
, en-tête = Vrai
, index = Faux
, taille de bloc = 100000
, compression = 'gzip'
, encoding = 'utf-8')
Choisissez la taille de bloc qui vous convient le mieux.
Passez à hdf < / a> format:
df.to_hdf (r'output.h5 ', mode =' w ')
Selon réponse krassowski , en utilisant numpy. Par exemple, en utilisant le df suivant:
df = pd.DataFrame ({'A': plage (1000000)})
df ['B'] = df.A + 1.0
df ['C'] = df.A + 2.0
df ['D'] = df.A + 3.0
Pandas en csv:
df.to_csv ('pandas_to_csv', index = False)
Sur mon ordinateur, prend 6,45 s ± 1,05 s par boucle (moyenne ± dev. standard de 7 courses, 1 boucle chacune) `
Numpy à csv:
savetxt (
'numpy_savetxt', aa.values, fmt = '% d,%. 1f,%. 1f,%. 1f',
header = ','. join (aa.columns), comments = '')
Sur mon ordinateur, prend 3,38 s ± 224 ms par boucle (moyenne ± dev. standard de 7 courses, 1 boucle chacune)
Utilisation de Pandaral·lel .
est un outil simple et efficace pour paralléliser votre calcul Pandas sur tous vos processeurs (Linux et MacOS uniquement). Comment accélérer considérablement le calcul de vos pandas avec une seule ligne de code. Cool!
Vous pouvez envisager de remplacer le dataframe Pandas par le dataframe DASK . Les API CSV sont très similaires aux pandas.
< / li>Salut, merci pour l'entrée mais je ne cherche pas à enregistrer les données dans un fichier. Je cherche uniquement à créer une liste de chaînes.
Je reçois une accélération d'environ 2,5 fois avec le multitraitement ...
def stash_df(df):
global the_df
the_df = df
def fn(i):
with StringIO() as fd:
np.savetxt(fd, the_df[i:i+N], fmt='%s', delimiter=',')
return fd.getvalue().split('\n')[:-1]
with multiprocessing.Pool(initializer=stash_df, initargs=(df,)) as pool:
result = []
for a in pool.map(fn, range(0, len(df), N)):
result.extend(a)
réduit le temps global de 1 million de lignes de 6,8 secondes à 2,8 secondes sur mon ordinateur portable. j'espère passer à plus de cœurs dans un processeur i9.
Cela dépend de la sémantique Unix fork pour partager la trame de données avec les processus enfants, et fait évidemment un peu plus de travail, mais pourrait aider. ..
l'utilisation de la suggestion de numpy.savetxt de Massifox avec multiprocessing réduit cela à 2,0 secondes, juste map ce qui suit function:
def fn2(i):
with StringIO() as fd:
np.savetxt(fd, df[i:i+N], fmt='%s', delimiter=',')
return fd.getvalue().split('\n')[:-1]
le résultat est sinon fondamentalement le même
votre commentaire qui dit "le dataframe est une variable dans une classe" peut être corrigé dans une variété de différentes manières. un moyen simple consisterait simplement à transmettre le dataframe au Pool initializer auquel point il ne sera pas choisi (sous Unix de toute façon) et cacher une référence à lui dans une variable globale quelque part. cette référence peut ensuite être utilisée par chaque processus de travail, par exemple:
import multiprocessing
# df from OPs above code available in global scope
def fn(i):
return df[i:i+1000].to_csv(index=False, header=False).split('\n')[:-1]
with multiprocessing.Pool() as pool:
result = []
for a in pool.map(fn, range(0, len(df), 1000)):
result.extend(a)
cela ira tant que chaque Pool est utilisé par une seule dataframe
Pour une raison quelconque, la mise en œuvre du multitraitement est beaucoup plus lente à grande échelle. Je soupçonne que cela est dû à la canalisation des gros morceaux de trame de données entre le parent et les différents processus enfants. Cependant, la méthode numpy.savetext donne une accélération presque 2x ce qui est grandement apprécié!
en supposant que vous pouvez compter sur la fourche; le seul IPC appréciable devrait renvoyer des pickled des listes de chaînes de processus enfant à parent . vous voulez arranger les choses pour que la trame de données soit disponible dans les processus enfants après la création du Pool (c'est-à-dire non passée en argument à la fonction ped map )
Le problème est que le dataframe est une variable au sein d'une classe. Si la méthode à paralelliser fait partie de la classe, elle peut voir l'instance de dataframe mais ne peut pas être picklée. Afin de décaper la méthode, elle ne peut pas être une instance de classe, et ne peut donc pas voir le dataframe sans qu'il lui soit explicitement passé ...
L'utilisation de dictionnaires améliore légèrement les performances:
import multiprocessing
import numpy as np
import pandas pd
size = 100000
cols = list('abcdefghijklmnopqrstuvwxyz')
n_core = muliprocessing.cpu_count()
def format_col(col):
return col, ["%s_%d" % (col, n) for n in np.arange(size)]
with multiprocessing.Pool(n_core) as pool:
result = {}
for res in pool.map(format_col, cols):
result[res[0]]=res[1]
result.extend(res)
df = pd.DataFrame(result)
%%timeit
df = pd.DataFrame()
for col in cols:
df[col] = np.arange(size)
df[col] = "%s_" % col + df[col].astype(str)
# 1.91 s ± 84.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%%timeit
dict_res= {}
for col in cols:
dict_res[col] = ["%s_%d" % (col, n) for n in np.arange(size)]
df2 = pd.DataFrame(dict_res)
# 1.56 s ± 99 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
En utilisant le multitraitement, le code serait le suivant:
size = 100000
cols = list('abcdefghijklmnopqrstuvwxyz')
Maintenant, je ne peux pas l'exécuter sur mon ordinateur. Mais les performances s'amélioreront probablement.
Cela accélère la génération du dataframe, ce qui n'est pas la question / problème. J'ai besoin d'accélérer la conversion du dataframe en une liste de chaînes.
Essayez cette solution:
[s.replace(' ', ',') for s in list_of_string]
# output:
['a_0,b_1,c_1,d_1,e_1,f_1,g_1,h_1,i_1,j_1,k_1,l_1,m_1,n_1,o_1,p_1,q_1,r_1,s_1,t_1,u_1,v_1,w_1,x_1,y_1,z_1',
'a_1,b_2,c_2,d_2,e_2,f_2,g_2,h_2,i_2,j_2,k_2,l_2,m_2,n_2,o_2,p_2,q_2,r_2,s_2,t_2,u_2,v_2,w_2,x_2,y_2,z_2',
'a_2,b_3,c_3,d_3,e_3,f_3,g_3,h_3,i_3,j_3,k_3,l_3,m_3,n_3,o_3,p_3,q_3,r_3,s_3,t_3,u_3,v_3,w_3,x_3,y_3,z_3',
'a_3,b_4,c_4,d_4,e_4,f_4,g_4,h_4,i_4,j_4,k_4,l_4,m_4,n_4,o_4,p_4,q_4,r_4,s_4,t_4,u_4,v_4,w_4,x_4,y_4,z_4',
'a_4,b_5,c_5,d_5,e_5,f_5,g_5,h_5,i_5,j_5,k_5,l_5,m_5,n_5,o_5,p_5,q_5,r_5,s_5,t_5,u_5,v_5,w_5,x_5,y_5,z_5']
Si vous voulez remplacer l'espace blanc par une virgule:
list_of_string = df.head(5).set_index(cols[0]).to_string(header=False).split('\n')[1:]
# output:
['a_0 b_1 c_1 d_1 e_1 f_1 g_1 h_1 i_1 j_1 k_1 l_1 m_1 n_1 o_1 p_1 q_1 r_1 s_1 t_1 u_1 v_1 w_1 x_1 y_1 z_1',
'a_1 b_2 c_2 d_2 e_2 f_2 g_2 h_2 i_2 j_2 k_2 l_2 m_2 n_2 o_2 p_2 q_2 r_2 s_2 t_2 u_2 v_2 w_2 x_2 y_2 z_2',
'a_2 b_3 c_3 d_3 e_3 f_3 g_3 h_3 i_3 j_3 k_3 l_3 m_3 n_3 o_3 p_3 q_3 r_3 s_3 t_3 u_3 v_3 w_3 x_3 y_3 z_3',
'a_3 b_4 c_4 d_4 e_4 f_4 g_4 h_4 i_4 j_4 k_4 l_4 m_4 n_4 o_4 p_4 q_4 r_4 s_4 t_4 u_4 v_4 w_4 x_4 y_4 z_4',
'a_4 b_5 c_5 d_5 e_5 f_5 g_5 h_5 i_5 j_5 k_5 l_5 m_5 n_5 o_5 p_5 q_5 r_5 s_5 t_5 u_5 v_5 w_5 x_5 y_5 z_5']
Vous pouvez accélérer ce code avec le conseil que je vous ai donné dans les réponses précédentes .
Conseils: DASK, Pandaral · lel et le multitraitement sont vos amis!
pourquoi voudriez-vous faire ça? Je ferais de mon mieux pour garder autant de données principalement hors de la RAM, certainement analysées en types de données appropriés afin que tout puisse être exploité efficacement
J'en ai besoin pour les comparaisons d'entropie et d'informations entre deux listes de chaînes.