Pandalar: önceden var olan bir sütundan hesaplanan değerlerle bir veri çerçevesinde iki yeni sütun oluşturun


100

Ben ile çalışıyorum pandalar kütüphane ve ben bir dataframe iki yeni sütun eklemek istediğiniz dfn sütunda (n> 0) ile.
Bu yeni sütunlar, veri çerçevesindeki sütunlardan birine bir işlevin uygulanmasından kaynaklanır.

Uygulanacak işlev şuna benzer:

def calculate(x):
    ...operate...
    return z, y

Yalnızca bir değer döndüren bir işlev için yeni bir sütun oluşturmanın bir yöntemi şudur:

df['new_col']) = df['column_A'].map(a_function)

Yani, istediğim ve başarısızlıkla denediğim (*) şunun gibi bir şey:

(df['new_col_zetas'], df['new_col_ys']) = df['column_A'].map(calculate)

Bunu başarmanın en iyi yolu ne olabilir? Ben taranan belgeleri hiçbir ipucu.

** df['column_A'].map(calculate)her öğe bir tuple z, y'den oluşan bir Pandalar Serisi döndürür. Ve bunu iki veri çerçevesi sütununa atamaya çalışmak bir ValueError üretir. *

Yanıtlar:


119

Sadece kullanırım zip:

In [1]: from pandas import *

In [2]: def calculate(x):
   ...:     return x*2, x*3
   ...: 

In [3]: df = DataFrame({'a': [1,2,3], 'b': [2,3,4]})

In [4]: df
Out[4]: 
   a  b
0  1  2
1  2  3
2  3  4

In [5]: df["A1"], df["A2"] = zip(*df["a"].map(calculate))

In [6]: df
Out[6]: 
   a  b  A1  A2
0  1  2   2   3
1  2  3   4   6
2  3  4   6   9

Teşekkürler, harika, işe yarıyor. 0.8.1 için dokümanlarda buna benzer bir şey bulamadım ... Sanırım Serileri her zaman tuple listeleri olarak düşünmeliyim ...
joaquin

Bunun yerine bunu yaparken performans farkı var mı? zip (* harita (hesapla, df ["a"])) yerine zip (* df ["a"]. harita (hesapla)), bu da (yukarıdaki gibi) [(2, 4, 6), ( 3, 6, 9)]?
ekta

1
Şu şekilde yeni sütun oluştururken şu uyarıları alıyorum: "SettingWithCopyWarning: DataFrame'den bir dilim kopyasında bir değer ayarlanmaya çalışıyor. Bunun yerine .loc [row_indexer, col_indexer] = değeri kullanmayı deneyin." Bunun için endişelenmeli miyim? pandas v.0.15
taras

47

En iyi cevap bence kusurlu. Umarım hiç kimse tüm pandaları kendi ad alanına toplu olarak aktarmıyor from pandas import *. Ayrıca, mapyöntem bir sözlüğü veya Diziyi geçerken bu zamanlar için ayrılmalıdır. Bir işlevi alabilir, ancak bunun applyiçin kullanılır.

Yani, yukarıdaki yaklaşımı kullanmanız gerekiyorsa, bunu şöyle yazardım

df["A1"], df["A2"] = zip(*df["a"].apply(calculate))

Aslında burada zip kullanmak için bir neden yok. Bunu basitçe yapabilirsiniz:

df["A1"], df["A2"] = calculate(df['a'])

Bu ikinci yöntem ayrıca daha büyük DataFrame'lerde çok daha hızlıdır

df = pd.DataFrame({'a': [1,2,3] * 100000, 'b': [2,3,4] * 100000})

DataFrame 300.000 satırla oluşturuldu

%timeit df["A1"], df["A2"] = calculate(df['a'])
2.65 ms ± 92.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
159 ms ± 5.24 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Zip'ten 60 kat daha hızlı


Genel olarak, uygula kullanmaktan kaçının

Uygula, genellikle bir Python listesi üzerinde yinelemekten çok daha hızlı değildir. Yukarıdaki ile aynı şeyi yapmak için bir for-döngüsünün performansını test edelim

%%timeit
A1, A2 = [], []
for val in df['a']:
    A1.append(val**2)
    A2.append(val**3)

df['A1'] = A1
df['A2'] = A2

298 ms ± 7.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Yani bu iki kat daha yavaş, bu korkunç bir performans gerilemesi değil, ancak yukarıdakileri şifrelersek çok daha iyi performans elde ederiz. Varsayalım ki, ipython kullanıyorsunuz:

%load_ext cython

%%cython
cpdef power(vals):
    A1, A2 = [], []
    cdef double val
    for val in vals:
        A1.append(val**2)
        A2.append(val**3)

    return A1, A2

%timeit df['A1'], df['A2'] = power(df['a'])
72.7 ms ± 2.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Başvurmadan doğrudan atama

Doğrudan vektörleştirilmiş işlemleri kullanırsanız daha da büyük hız iyileştirmeleri elde edebilirsiniz.

%timeit df['A1'], df['A2'] = df['a'] ** 2, df['a'] ** 3
5.13 ms ± 320 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Bu, döngülerimiz yerine NumPy'nin son derece hızlı vektörleştirilmiş işlemlerinden yararlanır. Artık orijinaline göre 30x hızlanma var.


En basit hız testi apply

Yukarıdaki örnek, ne kadar yavaş applyolabileceğini açıkça göstermelidir , ancak çok net olması için en temel örneğe bakalım. 10 milyon sayılık bir seriyi uygulayarak ve uygulamadan kare yapalım

s = pd.Series(np.random.rand(10000000))

%timeit s.apply(calc)
3.3 s ± 57.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Başvurmadan 50 kat daha hızlıdır

%timeit s ** 2
66 ms ± 2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

1
Bu gerçekten harika bir cevap. Şu applymapsoruyu sormak istiyorum: Veri çerçevesinin her bir öğesine belirli bir işlevi uygulamak zorunda kaldığınızda durum için ne düşünüyorsunuz ?
David

3
Bu yanıtta bazı iyi tavsiyeler olsa da, func(series)bunun yerine kullanılacak ana tavsiyenin series.apply(func), yalnızca işlev, hem bireysel bir değerde hem de Seri üzerinde benzer şekilde davranan işlemler kullanılarak tamamen tanımlandığında uygulanabilir olduğuna inanıyorum . İlk cevaptaki örnekte durum budur, ancak OP'nin sorusunda durum böyle değildir, daha genel olarak sütunlara fonksiyon uygulama hakkında soru sormaktadır. 1/2
Graham Lea

1
Örnek olarak, eğer df: DataFrame({'a': ['Aaron', 'Bert', 'Christopher'], 'b': ['Bold', 'Courageous', 'Distrusted']})ve calcise: def calc(x): return x[0], len(x)o zaman tdf.a.apply(calc))ve calc(tdf.a)çok farklı şeyler döndür.
Graham Lea
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.