Cómo formar una columna de tupla a partir de dos columnas en Pandas

125

Tengo un Pandas DataFrame y quiero combinar las columnas 'lat' y 'long' para formar una tupla.

<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)

El código que intenté usar fue:

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

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

Sin embargo, esto devolvió el siguiente error:

---------------------------------------------------------------------------
 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 

¿Como puedó resolver esté problema?

elksie5000
fuente

Respuestas:

201

Póngase cómodo con zip. Resulta útil cuando se trata de datos de columna.

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

Es menos complicado y más rápido que usar applyo map. Algo como np.dstackes dos veces más rápido que zip, pero no daría tuplas.

Dale Jung
fuente
3
en python3, tienes que usar list. Esto debería funcionar:df['new_col'] = list(zip(df.lat, df.long))
paulwasit
@paulwasit ah sí, mi relación de amor y odio con el comportamiento perezoso de Python 3. Gracias.
Dale Jung
4
Este método list(zip(df.lat, df.long))en 124ms es mucho más eficiente que df[['lat', 'long']].apply(tuple, axis=1)en 14.2 s para 900k filas. La proporción es más de 100.
Pengju Zhao
1
Estoy tratando de usar esto con una lista más larga de columnas df['new_col'] = list(zip(df[cols_to_keep])) pero sigo recibiendo un error: ¿ Length of values does not match length of indexalgún consejo?
seeiespi
1
La respuesta de @ PeterHansen me ayudó, pero creo que puede haber faltado un * para descomprimir la lista primero, es decir df['new_col'] = list(zip(*[df[c] for c in cols_to_keep])
jedge
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
fuente
Eso es brillante. Gracias. Claramente necesito entender las funciones lambda.
elksie5000
¿Funcionó esto con tus datos? Si es así, ¿puede compartir su versión de pandas y los datos? Me pregunto por qué su código no funcionó, debería.
Wouter Overmeire
La versión es 0.10.1_20130131. Disculpe mi ignorancia, pero ¿cuál es la mejor manera de cargar una sección de los datos para usted? (Todavía un novato relativo).
elksie5000
No pude reproducir en 0.10.1. ¿La mejor forma de subir? Puede crear un código que genere un marco que contenga datos aleatorios, que tenga el mismo problema y compartir ese código o seleccionar el marco de arriba (muestra) y transferirlo a través de un servicio gratuito de transferencia de archivos grandes. Cómo encurtir (en dos líneas, sin ","): importar encurtidos, con open ('sample.pickle', 'w') como archivo: pickle.dump (muestra, archivo)
Wouter Overmeire
1
He votado a favor de esto porque necesito comprimir 10 columnas y no quiero dar el nombre del marco de datos 10 veces. Solo quiero dar nombres de columnas.
rishi jain
13

Pandas tiene el itertuplesmétodo para hacer exactamente esto:

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

Me gustaría agregar df.values.tolist(). (siempre que no le importe obtener una columna de listas en lugar de tuplas)

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)
usuario3820991
fuente
Cuando se tiene algo más que estas dos columnas: %timeit df[['a', 'b']].values.tolist(). Sigue siendo mucho más rápido.
ChaimG