So bilden Sie eine Tupelspalte aus zwei Spalten in Pandas

125

Ich habe einen Pandas DataFrame und möchte die Spalten 'lat' und 'long' zu einem Tupel kombinieren.

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

Der Code, den ich zu verwenden versuchte, war:

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

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

Dies ergab jedoch den folgenden Fehler:

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

Wie kann ich dieses Problem lösen?

elksie5000
quelle

Antworten:

200

Machen Sie es sich bequem mit zip. Dies ist praktisch, wenn Sie mit Spaltendaten arbeiten.

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

Es ist weniger kompliziert und schneller als die Verwendung von applyoder map. So etwas np.dstackist doppelt so schnell wie zip, würde dir aber keine Tupel geben.

Dale Jung
quelle
3
In Python3 müssen Sie verwenden list. Dies sollte funktionieren:df['new_col'] = list(zip(df.lat, df.long))
Paulwasit
@ Paulwasit ah ja, meine Liebe hasst Beziehung mit dem faulen Verhalten von Python 3. Vielen Dank.
Dale Jung
4
Diese Methode ist list(zip(df.lat, df.long))in 124 df[['lat', 'long']].apply(tuple, axis=1)ms für 900.000 Zeilen viel effizienter als in 14,2 s. Das Verhältnis ist mehr als 100.
Pengju Zhao
1
Ich versuche, dies mit einer längeren Liste von Spalten zu verwenden df['new_col'] = list(zip(df[cols_to_keep])) , erhalte aber immer wieder eine Fehlermeldung: Length of values does not match length of indexIrgendwelche Ratschläge?
Seeiespi
1
@ PeterHansens Antwort hat mir geholfen, aber ich denke, es hat möglicherweise ein * gefehlt, um die Liste zuerst df['new_col'] = list(zip(*[df[c] for c in cols_to_keep])
auszupacken
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
quelle
Das ist hervorragend. Danke dir. Ich muss mich eindeutig mit Lambda-Funktionen beschäftigen.
Elksie5000
Hat das bei Ihren Daten funktioniert? Wenn ja, können Sie Ihre Pandas-Version und die Daten teilen? Ich frage mich, warum Ihr Code nicht funktioniert hat, sollte es.
Wouter Overmeire
Die Version ist 0.10.1_20130131. Entschuldigen Sie meine Unwissenheit, aber wie können Sie einen Teil der Daten am besten für Sie hochladen? (Immer noch ein relativer Neuling).
Elksie5000
Ich konnte am 0.10.1 nicht reproduzieren. Beste Art des Hochladens? Sie können entweder Code erstellen, der einen Frame mit zufälligen Daten generiert, der das gleiche Problem aufweist, und diesen Code freigeben, oder den obigen Frame (Beispiel) auswählen und über einen kostenlosen Übertragungsdienst für große Dateien übertragen. So beizen Sie (in zwei Zeilen, ohne ","): importieren Sie pickle mit open ('sample.pickle', 'w') als Datei: pickle.dump (sample, file)
Wouter Overmeire
1
Ich habe dies positiv bewertet, da ich 10 Spalten komprimieren muss und nicht 10 Mal den Namen des Datenrahmens angeben möchte. Ich möchte nur Spaltennamen angeben.
Rishi Jain
13

Pandas hat die itertuplesMethode, genau dies zu tun:

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

Ich möchte hinzufügen df.values.tolist(). (Solange es Ihnen nichts ausmacht, eine Liste mit Listen anstelle von Tupeln zu erhalten)

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
quelle
Wenn Sie mehr als nur diese beiden Spalten haben : %timeit df[['a', 'b']].values.tolist(). Es ist immer noch viel schneller.
ChaimG