Comment former une colonne tuple à partir de deux colonnes dans Pandas

125

J'ai un Pandas DataFrame et je veux combiner les colonnes «lat» et «long» pour former un tuple.

<class 'pandas.core.frame.DataFrame'>
Int64Index: 205482 entries, 0 to 209018
Data columns:
Month           205482  non-null values
Reported by     205482  non-null values
Falls within    205482  non-null values
Easting         205482  non-null values
Northing        205482  non-null values
Location        205482  non-null values
Crime type      205482  non-null values
long            205482  non-null values
lat             205482  non-null values
dtypes: float64(4), object(5)

Le code que j'ai essayé d'utiliser était:

def merge_two_cols(series): 
    return (series['lat'], series['long'])

sample['lat_long'] = sample.apply(merge_two_cols, axis=1)

Cependant, cela a renvoyé l'erreur suivante:

---------------------------------------------------------------------------
 AssertionError                            Traceback (most recent call last)
<ipython-input-261-e752e52a96e6> in <module>()
      2     return (series['lat'], series['long'])
      3 
----> 4 sample['lat_long'] = sample.apply(merge_two_cols, axis=1)
      5

...

AssertionError: Block shape incompatible with manager 

Comment puis-je résoudre ce problème?

elksie5000
la source

Réponses:

201

Soyez à l'aise avec zip. Il est pratique lorsqu'il s'agit de données de colonne.

df['new_col'] = list(zip(df.lat, df.long))

C'est moins compliqué et plus rapide que d'utiliser applyou map. Quelque chose comme np.dstackest deux fois plus rapide que zip, mais ne vous donnerait pas de tuples.

Dale Jung
la source
3
en python3, vous devez utiliser list. Cela devrait fonctionner:df['new_col'] = list(zip(df.lat, df.long))
paulwasit
@paulwasit ah oui, mon amour déteste la relation avec le comportement paresseux de python 3. Merci.
Dale Jung du
4
Cette méthode list(zip(df.lat, df.long))en 124 ms est beaucoup plus efficace qu'en df[['lat', 'long']].apply(tuple, axis=1)14,2 s pour 900 000 lignes. Le ratio est supérieur à 100.
Pengju Zhao
1
J'essaie de l'utiliser avec une liste de colonnes plus longue df['new_col'] = list(zip(df[cols_to_keep])) mais je reçois toujours une erreur: Length of values does not match length of indexun conseil?
seeiespi
1
La réponse de @ PeterHansen m'a aidé, mais je pense qu'il manquait peut-être un * pour déballer la liste en premier - c'estdf['new_col'] = list(zip(*[df[c] for c in cols_to_keep])
jedge le
61
In [10]: df
Out[10]:
          A         B       lat      long
0  1.428987  0.614405  0.484370 -0.628298
1 -0.485747  0.275096  0.497116  1.047605
2  0.822527  0.340689  2.120676 -2.436831
3  0.384719 -0.042070  1.426703 -0.634355
4 -0.937442  2.520756 -1.662615 -1.377490
5 -0.154816  0.617671 -0.090484 -0.191906
6 -0.705177 -1.086138 -0.629708  1.332853
7  0.637496 -0.643773 -0.492668 -0.777344
8  1.109497 -0.610165  0.260325  2.533383
9 -1.224584  0.117668  1.304369 -0.152561

In [11]: df['lat_long'] = df[['lat', 'long']].apply(tuple, axis=1)

In [12]: df
Out[12]:
          A         B       lat      long                             lat_long
0  1.428987  0.614405  0.484370 -0.628298      (0.484370195967, -0.6282975278)
1 -0.485747  0.275096  0.497116  1.047605      (0.497115615839, 1.04760475074)
2  0.822527  0.340689  2.120676 -2.436831      (2.12067574274, -2.43683074367)
3  0.384719 -0.042070  1.426703 -0.634355      (1.42670326172, -0.63435462504)
4 -0.937442  2.520756 -1.662615 -1.377490     (-1.66261469102, -1.37749004179)
5 -0.154816  0.617671 -0.090484 -0.191906  (-0.0904840623396, -0.191905582481)
6 -0.705177 -1.086138 -0.629708  1.332853     (-0.629707821728, 1.33285348929)
7  0.637496 -0.643773 -0.492668 -0.777344   (-0.492667604075, -0.777344111021)
8  1.109497 -0.610165  0.260325  2.533383        (0.26032456699, 2.5333825651)
9 -1.224584  0.117668  1.304369 -0.152561     (1.30436900612, -0.152560909725)
Wouter Overmeire
la source
C'est brillant. Je vous remercie. Je dois clairement comprendre les fonctions lambda.
elksie5000
Cela a-t-il fonctionné sur vos données? Si oui, pouvez-vous partager votre version de pandas et les données? Je me demande pourquoi votre code n'a pas fonctionné, il devrait.
Wouter Overmeire
La version est 0.10.1_20130131. Excusez mon ignorance, mais quelle est la meilleure façon de télécharger une section des données pour vous? (Encore un débutant relatif).
elksie5000
Je n'ai pas réussi à reproduire sur 0.10.1. Meilleur moyen de télécharger? Vous pouvez soit créer un code qui génère une image contenant des données aléatoires, qui présente le même problème et partager ce code, soit décaper l'image ci-dessus (exemple) et la transférer via un service gratuit de transfert de gros fichiers. Comment pickle (en deux lignes, sans ","): import pickle, avec open ('sample.pickle', 'w') comme fichier: pickle.dump (sample, file)
Wouter Overmeire
1
J'ai voté pour cela car j'ai besoin de compresser 10 colonnes et je ne veux pas donner le nom du dataframe 10 fois. Je veux juste donner des noms de colonnes.
rishi jain
13

Pandas a la itertuplesméthode pour faire exactement cela:

list(df[['lat', 'long']].itertuples(index=False, name=None))
Ted Petrou
la source
3

Je voudrais ajouter df.values.tolist(). (tant que cela ne vous dérange pas d'obtenir une colonne de listes plutôt que des tuples)

import pandas as pd
import numpy as np

size = int(1e+07)
df = pd.DataFrame({'a': np.random.rand(size), 'b': np.random.rand(size)}) 

%timeit df.values.tolist()
1.47 s ± 38.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit list(zip(df.a,df.b))
1.92 s ± 131 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
user3820991
la source
Lorsque vous avez plus que ces deux colonnes: %timeit df[['a', 'b']].values.tolist(). C'est encore beaucoup plus rapide.
ChaimG le