181 votes

Pandas convertit un dataframe en un tableau de tuples.

J'ai manipulé certaines données à l'aide de pandas et je veux maintenant effectuer une sauvegarde par lot dans la base de données. Pour cela, je dois convertir le cadre de données en un tableau de tuples, chaque tuple correspondant à une "ligne" du cadre de données.

Mon DataFrame ressemble à quelque chose comme :

In [182]: data_set
Out[182]: 
  index data_date   data_1  data_2
0  14303 2012-02-17  24.75   25.03 
1  12009 2012-02-16  25.00   25.07 
2  11830 2012-02-15  24.99   25.15 
3  6274  2012-02-14  24.68   25.05 
4  2302  2012-02-13  24.62   24.77 
5  14085 2012-02-10  24.38   24.61 

Je veux le convertir en un tableau de tuples comme :

[(datetime.date(2012,2,17),24.75,25.03),
(datetime.date(2012,2,16),25.00,25.07),
...etc. ]

Avez-vous des suggestions sur la façon dont je peux faire cela efficacement ?

36 votes

Pour ceux qui viennent à cette réponse en 2017+, il y a une nouvelle solution idiomatique ci-dessous . Vous pouvez simplement utiliser list(df.itertuples(index=False, name=None))

5 votes

Les deux choses que je recherche lorsque je me pose cette question : Une liste de tuples - df.to_records(index=False) et une liste de dicts : df.to_dict('records')

1 votes

@MartinThoma Les deux to_records et to_dict('records') bousillent mes types de données. Bug connu mais qui rend cette solution inutile...

243voto

Wes McKinney Points 17545

Pourquoi pas :

subset = data_set[['data_date', 'data_1', 'data_2']]
tuples = [tuple(x) for x in subset.to_numpy()]

pour pandas < 0.24 utiliser

tuples = [tuple(x) for x in subset.values]

5 votes

Veuillez consulter la réponse de @ksindi ci-dessous pour utiliser .itertuples ce qui sera plus efficace que de récupérer les valeurs sous forme de tableau et de les transformer en tuple.

3 votes

Un peu plus propre est : tuples=map(tuple,subset.values)

0 votes

Cela peut cependant convertir des valeurs en un type différent, n'est-ce pas ?

230voto

tuva Points 885
list(data_set.itertuples(index=False))

À partir de la version 17.1, l'option ci-dessus renverra un message de type liste de tuples nommés .

Si vous voulez une liste de tuples ordinaires, passez la commande name=None comme argument :

list(data_set.itertuples(index=False, name=None))

50 votes

Ce devrait être la réponse acceptée IMHO (maintenant qu'une fonction dédiée existe). Par ailleurs, si vous voulez que les tuple dans votre zip (au lieu de l'itérateur namedtuple ), puis appelez : data_set.itertuples(index=False, name=None)

2 votes

4 votes

@coldspeed La leçon que je tire de la question liée est que itertuples est lent parce que la conversion en tuples est généralement plus lente que les opérations vectorielles/cython. Étant donné que la question demande de convertir en tuples, y a-t-il une raison de penser que la réponse acceptée est plus rapide ? Le test rapide que j'ai effectué indique que la version itertuples est plus rapide.

47voto

rrv Points 398

Une manière générique :

[tuple(x) for x in data_set.to_records(index=False)]

3 votes

N'est-ce pas ? data_set.to_records(index=False).tolist() mieux ?

43voto

piRSquared Points 159

Motivation
De nombreux ensembles de données sont suffisamment volumineux pour que nous devions nous préoccuper de la vitesse et de l'efficacité. C'est dans cet esprit que je propose cette solution. Il se trouve qu'elle est également succincte.

Pour les besoins de la comparaison, laissons tomber l'élément index colonne

df = data_set.drop('index', 1)

Solution
Je vais proposer l'utilisation de zip et map

list(zip(*map(df.get, df)))

[('2012-02-17', 24.75, 25.03),
 ('2012-02-16', 25.0, 25.07),
 ('2012-02-15', 24.99, 25.15),
 ('2012-02-14', 24.68, 25.05),
 ('2012-02-13', 24.62, 24.77),
 ('2012-02-10', 24.38, 24.61)]

Il se trouve qu'elle est également flexible si nous voulons traiter un sous-ensemble spécifique de colonnes. Nous supposerons que les colonnes que nous avons déjà affichées sont le sous-ensemble que nous voulons.

list(zip(*map(df.get, ['data_date', 'data_1', 'data_2'])))

[('2012-02-17', 24.75, 25.03),
 ('2012-02-16', 25.0, 25.07),
 ('2012-02-15', 24.99, 25.15),
 ('2012-02-14', 24.68, 25.05),
 ('2012-02-13', 24.62, 24.77),
 ('2012-02-10', 24.38, 24.61)]

Qu'est-ce qui est plus rapide ?

C'est fini. records est le plus rapide, suivi d'une convergence asymptotique. zipmap et iter_tuples

Je vais utiliser une bibliothèque simple_benchmarks que j'ai obtenu de ce poste

from simple_benchmark import BenchmarkBuilder
b = BenchmarkBuilder()

import pandas as pd
import numpy as np

def tuple_comp(df): return [tuple(x) for x in df.to_numpy()]
def iter_namedtuples(df): return list(df.itertuples(index=False))
def iter_tuples(df): return list(df.itertuples(index=False, name=None))
def records(df): return df.to_records(index=False).tolist()
def zipmap(df): return list(zip(*map(df.get, df)))

funcs = [tuple_comp, iter_namedtuples, iter_tuples, records, zipmap]
for func in funcs:
    b.add_function()(func)

def creator(n):
    return pd.DataFrame({"A": random.randint(n, size=n), "B": random.randint(n, size=n)})

@b.add_arguments('Rows in DataFrame')
def argument_provider():
    for n in (10 ** (np.arange(4, 11) / 2)).astype(int):
        yield n, creator(n)

r = b.run()

Vérifiez les résultats

r.to_pandas_dataframe().pipe(lambda d: d.div(d.min(1), 0))

        tuple_comp  iter_namedtuples  iter_tuples   records    zipmap
100       2.905662          6.626308     3.450741  1.469471  1.000000
316       4.612692          4.814433     2.375874  1.096352  1.000000
1000      6.513121          4.106426     1.958293  1.000000  1.316303
3162      8.446138          4.082161     1.808339  1.000000  1.533605
10000     8.424483          3.621461     1.651831  1.000000  1.558592
31622     7.813803          3.386592     1.586483  1.000000  1.515478
100000    7.050572          3.162426     1.499977  1.000000  1.480131

r.plot()

enter image description here

13voto

Nickil Maveli Points 16776

Voici une approche vectorielle (en supposant que le cadre de données, data_set à définir comme df au lieu de) qui renvoie un list de tuples comme indiqué :

>>> df.set_index(['data_date'])[['data_1', 'data_2']].to_records().tolist()

produit :

[(datetime.datetime(2012, 2, 17, 0, 0), 24.75, 25.03),
 (datetime.datetime(2012, 2, 16, 0, 0), 25.0, 25.07),
 (datetime.datetime(2012, 2, 15, 0, 0), 24.99, 25.15),
 (datetime.datetime(2012, 2, 14, 0, 0), 24.68, 25.05),
 (datetime.datetime(2012, 2, 13, 0, 0), 24.62, 24.77),
 (datetime.datetime(2012, 2, 10, 0, 0), 24.38, 24.61)]

L'idée de définir une colonne de date comme axe d'indexation est d'aider à la conversion de l'axe d'indexation de la colonne de date. Timestamp à sa valeur correspondante datetime.datetime en faisant appel à l'équivalent du format convert_datetime64 argument dans DF.to_records qui le fait pour un DateTimeIndex cadre de données.

Cela renvoie un recarray qui pourrait alors retourner un list en utilisant .tolist


Une solution plus généralisée en fonction du cas d'utilisation serait :

df.to_records().tolist()                              # Supply index=False to exclude index

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X