Ich habe einen Pandas-Datenrahmen (dies ist nur ein kleines Stück)
>>> d1
y norm test y norm train len(y_train) len(y_test) \
0 64.904368 116.151232 1645 549
1 70.852681 112.639876 1645 549
SVR RBF \
0 (35.652207342877873, 22.95533537448393)
1 (39.563683797747622, 27.382483096332511)
LCV \
0 (19.365430594452338, 13.880062435173587)
1 (19.099614489458364, 14.018867136617146)
RIDGE CV \
0 (4.2907610988480362, 12.416745648065584)
1 (4.18864306788194, 12.980833914392477)
RF \
0 (9.9484841581029428, 16.46902345373697)
1 (10.139848213735391, 16.282141345406522)
GB \
0 (0.012816232716538605, 15.950164822266007)
1 (0.012814519804493328, 15.305745202851712)
ET DATA
0 (0.00034337162272515505, 16.284800366214057) j2m
1 (0.00024811554516431878, 15.556506191784194) j2m
>>>
Ich möchte alle Spalten teilen, die Tupel enthalten. Zum Beispiel möchte ich die Spalte LCV
durch die Spalten LCV-a
und ersetzen LCV-b
.
Wie kann ich das machen?
Bei viel größeren Datensätzen stellte ich fest, dass
.apply()
nur wenige Bestellungen langsamer sind alspd.DataFrame(df['b'].values.tolist(), index=df.index)
Dieses Leistungsproblem wurde in GitHub geschlossen, obwohl ich dieser Entscheidung nicht zustimme:
https://github.com/pandas-dev/pandas/issues/11615
BEARBEITEN: basierend auf dieser Antwort: https://stackoverflow.com/a/44196843/2230844
quelle
pd.DataFrame(df['b'].tolist())
ohne das.values
scheint auch gut zu funktionieren. (Und danke, Ihre Lösung ist viel schneller als.apply()
)Der
str
Accessor, der fürpandas.Series
Objekte von verfügbar ist,dtype == object
ist tatsächlich iterierbar.Angenommen, a
pandas.DataFrame
df
:df = pd.DataFrame(dict(col=[*zip('abcdefghij', range(10, 101, 10))])) df col 0 (a, 10) 1 (b, 20) 2 (c, 30) 3 (d, 40) 4 (e, 50) 5 (f, 60) 6 (g, 70) 7 (h, 80) 8 (i, 90) 9 (j, 100)
Wir können testen, ob es iterierbar ist
from collections import Iterable isinstance(df.col.str, Iterable) True
Wir können es dann wie andere Iterables zuweisen:
var0, var1 = 'xy' print(var0, var1) x y
Einfachste Lösung
In einer Zeile können wir also beide Spalten zuweisen
df['a'], df['b'] = df.col.str df col a b 0 (a, 10) a 10 1 (b, 20) b 20 2 (c, 30) c 30 3 (d, 40) d 40 4 (e, 50) e 50 5 (f, 60) f 60 6 (g, 70) g 70 7 (h, 80) h 80 8 (i, 90) i 90 9 (j, 100) j 100
Schnellere Lösung
Nur etwas komplizierter, können wir verwenden
zip
, um eine ähnliche iterable zu erstellendf['c'], df['d'] = zip(*df.col) df col a b c d 0 (a, 10) a 10 a 10 1 (b, 20) b 20 b 20 2 (c, 30) c 30 c 30 3 (d, 40) d 40 d 40 4 (e, 50) e 50 e 50 5 (f, 60) f 60 f 60 6 (g, 70) g 70 g 70 7 (h, 80) h 80 h 80 8 (i, 90) i 90 i 90 9 (j, 100) j 100 j 100
In der Reihe
Bedeutung, vorhandene nicht mutieren
df
Dies funktioniert, da
assign
Schlüsselwortargumente verwendet werden, bei denen die Schlüsselwörter die neuen (oder vorhandenen) Spaltennamen sind und die Werte die Werte der neuen Spalte sind. Sie können ein Wörterbuch verwenden und es entpacken**
und als Schlüsselwortargumente verwenden. Dies ist also eine clevere Möglichkeit, eine neue Spalte mit dem Namen zuzuweisen , die'g'
das erste Element in derdf.col.str
Iterable und'h'
das zweite Element in derdf.col.str
Iterable ist.df.assign(**dict(zip('gh', df.col.str))) col g h 0 (a, 10) a 10 1 (b, 20) b 20 2 (c, 30) c 30 3 (d, 40) d 40 4 (e, 50) e 50 5 (f, 60) f 60 6 (g, 70) g 70 7 (h, 80) h 80 8 (i, 90) i 90 9 (j, 100) j 100
Meine Version des
list
AnsatzesMit modernem Listenverständnis und variablem Auspacken.
Hinweis: auch inline mit
join
df.join(pd.DataFrame([*df.col], df.index, [*'ef'])) col g h 0 (a, 10) a 10 1 (b, 20) b 20 2 (c, 30) c 30 3 (d, 40) d 40 4 (e, 50) e 50 5 (f, 60) f 60 6 (g, 70) g 70 7 (h, 80) h 80 8 (i, 90) i 90 9 (j, 100) j 100
Die mutierende Version wäre
df[['e', 'f']] = pd.DataFrame([*df.col], df.index)
Naiver Zeittest
Kurzer DataFrameVerwenden Sie eine oben definierte
Langer DataFrame%timeit df.assign(**dict(zip('gh', df.col.str))) %timeit df.assign(**dict(zip('gh', zip(*df.col)))) %timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh'])) 1.16 ms ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 635 µs ± 18.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 795 µs ± 42.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
10 ^ 3 mal größer
df = pd.concat([df] * 1000, ignore_index=True) %timeit df.assign(**dict(zip('gh', df.col.str))) %timeit df.assign(**dict(zip('gh', zip(*df.col)))) %timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh'])) 11.4 ms ± 1.53 ms per loop (mean ± std. dev. of 7 runs, 100 loops each) 2.1 ms ± 41.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 2.33 ms ± 35.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
quelle
df['a'], df['b'] = df.col.str
Ich denke, ein einfacher Weg ist:
>>> import pandas as pd >>> df = pd.DataFrame({'a':[1,2], 'b':[(1,2), (3,4)]}) >>> df a b 0 1 (1, 2) 1 2 (3, 4) >>> df['b_a']=df['b'].str[0] >>> df['b_b']=df['b'].str[1] >>> df a b b_a b_b 0 1 (1, 2) 1 2 1 2 (3, 4) 3 4
quelle
str
Darstellung einespd.Series
Objekts ist. Können Sie erklären, wie das überhaupt funktioniert?!Ich weiß, dass dies von vor einiger Zeit ist, aber eine Einschränkung der zweiten Lösung:
pd.DataFrame(df['b'].values.tolist())
ist, dass der Index explizit verworfen und ein sequentieller Standardindex hinzugefügt wird, während die akzeptierte Antwort
wird nicht, da das Ergebnis von apply den Zeilenindex beibehält. Während die Reihenfolge zunächst vom ursprünglichen Array beibehalten wird, versuchen Pandas, die Angaben aus den beiden Datenrahmen abzugleichen.
Dies kann sehr wichtig sein, wenn Sie versuchen, die Zeilen in ein numerisch indiziertes Array zu setzen, und Pandas automatisch versuchen, den Index des neuen Arrays mit dem alten abzugleichen, und eine gewisse Verzerrung in der Reihenfolge verursachen.
Eine bessere Hybridlösung wäre, den Index des ursprünglichen Datenrahmens auf den neuen zu setzen, d. H.
pd.DataFrame(df['b'].values.tolist(), index=df.index)
Dadurch bleibt die Geschwindigkeit der Verwendung der zweiten Methode erhalten, während sichergestellt wird, dass die Reihenfolge und die Indizierung für das Ergebnis beibehalten werden.
quelle