Pandas'ta dataframe.apply'de önceki satır değerini kullanmanın bir yolu var mı?


95

Aşağıdaki veri çerçevesine sahibim:

 Index_Date    A    B    C    D
 ===============================
 2015-01-31    10   10   Nan  10
 2015-02-01     2    3   Nan  22 
 2015-02-02    10   60   Nan  280
 2015-02-03    10   100   Nan  250

Gereksinim:

 Index_Date    A    B    C    D
 ===============================
 2015-01-31    10   10   10   10
 2015-02-01     2    3   23   22
 2015-02-02    10   60   290  280
 2015-02-03    10   100  3000 250

Column Ctüretilmiştir 2015-01-31alarak valuearasında D.

Sonra kullanmak gerekir valueve Ciçin 2015-01-31ve çarpma ile valuearasında Aüzerinde 2015-02-01ve ekleyin B.

Ben bir applyve a shiftkullanarak if elsebunu denedim bir anahtar hatası veriyor.


Veri çerçevelerindeki son satırlarınız neden sütunlar için farklı Ave B?
Anton Protopopov

@Anton şimdi doğru olduğundan özür diler.
ctrl-alt-delete

Sütun Ave sütunda sonraki satırın değeri nedir D?
jezrael

7
Bu güzel bir soru. Benzer bir vektörleştirilmiş çözüme ihtiyacım var. Pandaların apply(), kullanıcının işlevinin hesaplamasının bir parçası olarak önceki satırdan bir veya daha fazla değere erişebildiği veya en azından bir sonraki yinelemede 'kendisine' iletilen bir değer döndürdüğü bir sürüm sunması iyi olurdu . Bu, for döngüsüne kıyasla bazı verimlilik kazanımlarına izin vermez mi?
Bill

@Bill, az önce eklediğim bu cevap ilginizi çekebilir , numbaburada genellikle iyi bir seçenektir.
jpp

Yanıtlar:


65

İlk olarak, türetilmiş değeri oluşturun:

df.loc[0, 'C'] = df.loc[0, 'D']

Ardından kalan satırları yineleyin ve hesaplanan değerleri doldurun:

for i in range(1, len(df)):
    df.loc[i, 'C'] = df.loc[i-1, 'C'] * df.loc[i, 'A'] + df.loc[i, 'B']


  Index_Date   A   B    C    D
0 2015-01-31  10  10   10   10
1 2015-02-01   2   3   23   22
2 2015-02-02  10  60  290  280

41
Pandalarda bunu döngü olmadan yapmak için bir işlev var mı?
ctrl-alt-delete

1
Girişlerin önceki adımların sonuçlarına bağlı olduğu hesaplamanın yinelemeli doğası, vektörleştirmeyi karmaşıklaştırır. applyDöngü ile aynı hesaplamayı yapan bir işlevle kullanabilirsiniz , ancak perde arkasında bu da bir döngü olacaktır. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…
Stefan

Bu döngüyü kullanırsam ve birleştirilmiş bir veri çerçevesi üzerinde hesaplarsam ve bir Nan bulursa çalışır, ancak yalnızca Nan ile satır için çalışır. Hiçbir hata atılmaz, fillNa denersem AttributeError: 'numpy.float64' nesnesinin 'fillna' niteliği yok Nan ile satırı atlamanın veya değerleri sıfıra ayarlamanın herhangi bir yolu var mı?
ctrl-alt-delete

Dışındaki sütunlarda eksik değerler mi demek istiyorsunuz C?
Stefan

Evet çözümünüz gayet iyi. Döngüden önce veri çerçevesindeki Nans'leri doldurduğumdan emin oluyorum.
ctrl-alt-delete

41

Bir sayı sütunu verildiğinde:

lst = []
cols = ['A']
for a in range(100, 105):
    lst.append([a])
df = pd.DataFrame(lst, columns=cols, index=range(5))
df

    A
0   100
1   101
2   102
3   103
4   104

Önceki satıra shift ile başvurabilirsiniz:

df['Change'] = df.A - df.A.shift(1)
df

    A   Change
0   100 NaN
1   101 1.0
2   102 1.0
3   103 1.0
4   104 1.0

9
Bu, bu durumda yardımcı olmayacaktır çünkü önceki satırdaki değer başlangıçta bilinmemektedir. Her yinelemede hesaplanmalı ve ardından bir sonraki yinelemede kullanılmalıdır.
Bill

6
Hala bu cevap için minnettarım çünkü bununla karşılaştım, önceki satırın değerini bildiğim bir durum aradım. Çok teşekkürler @kztd
Kevin Pauli

28

numba

numbaJIT derlemesini kullanan ve daha düşük seviyeli nesnelerle çalışan vektörelleştirilemeyen yinelemeli hesaplamalar için , genellikle büyük performans iyileştirmeleri sağlar. Yalnızca normal bir fordöngü tanımlamanız ve dekoratörü kullanmanız gerekir @njitveya (eski sürümler için) @jit(nopython=True):

Makul boyutlu bir veri çerçevesi için bu, normal bir fordöngüye kıyasla ~ 30 kat daha fazla performans artışı sağlar :

from numba import jit

@jit(nopython=True)
def calculator_nb(a, b, d):
    res = np.empty(d.shape)
    res[0] = d[0]
    for i in range(1, res.shape[0]):
        res[i] = res[i-1] * a[i] + b[i]
    return res

df['C'] = calculator_nb(*df[list('ABD')].values.T)

n = 10**5
df = pd.concat([df]*n, ignore_index=True)

# benchmarking on Python 3.6.0, Pandas 0.19.2, NumPy 1.11.3, Numba 0.30.1
# calculator() is same as calculator_nb() but without @jit decorator
%timeit calculator_nb(*df[list('ABD')].values.T)  # 14.1 ms per loop
%timeit calculator(*df[list('ABD')].values.T)     # 444 ms per loop

1
Bu harika! Önceki değerlerden değerleri sayan işlevimi hızlandırdım. Teşekkürler!
Artem Malikov

21

Özyinelemeli işlevi numpy dizilerine uygulamak, mevcut yanıttan daha hızlı olacaktır.

df = pd.DataFrame(np.repeat(np.arange(2, 6),3).reshape(4,3), columns=['A', 'B', 'D'])
new = [df.D.values[0]]
for i in range(1, len(df.index)):
    new.append(new[i-1]*df.A.values[i]+df.B.values[i])
df['C'] = new

Çıktı

      A  B  D    C
   0  1  1  1    1
   1  2  2  2    4
   2  3  3  3   15
   3  4  4  4   64
   4  5  5  5  325

3
Bu cevap, benzer bir hesaplamayla benim için mükemmel çalışıyor. Cumsum ve shift kombinasyonunu kullanmayı denedim ama bu çözüm çok daha iyi çalışıyor. Teşekkürler.
Simon

Bu benim için de mükemmel çalışıyor, teşekkürler. Birçok yineleme, yineleme, uygulama ve benzeri biçimlerle mücadele ediyordum ve bunu anlamak ve gerçekleştirmek kolay görünüyor.
chaim

9

Bu sorunun sorulmasından bu yana uzun zaman geçmesine rağmen, birisine yardımcı olacağını umarak cevabımı göndereceğim.

Sorumluluk reddi: Bu çözümün standart olmadığını biliyorum , ancak iyi çalıştığını düşünüyorum.

import pandas as pd
import numpy as np

data = np.array([[10, 2, 10, 10],
                 [10, 3, 60, 100],
                 [np.nan] * 4,
                 [10, 22, 280, 250]]).T
idx = pd.date_range('20150131', end='20150203')
df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx)
df
               A    B     C    D
 =================================
 2015-01-31    10   10    NaN  10
 2015-02-01    2    3     NaN  22 
 2015-02-02    10   60    NaN  280
 2015-02-03    10   100   NaN  250

def calculate(mul, add):
    global value
    value = value * mul + add
    return value

value = df.loc['2015-01-31', 'D']
df.loc['2015-01-31', 'C'] = value
df.loc['2015-02-01':, 'C'] = df.loc['2015-02-01':].apply(lambda row: calculate(*row[['A', 'B']]), axis=1)
df
               A    B     C     D
 =================================
 2015-01-31    10   10    10    10
 2015-02-01    2    3     23    22 
 2015-02-02    10   60    290   280
 2015-02-03    10   100   3000  250

Yani temelde applypandalardan bir ve önceki hesaplanan değeri takip eden global bir değişkenin yardımını kullanıyoruz.


Bir fordöngü ile zaman karşılaştırması :

data = np.random.random(size=(1000, 4))
idx = pd.date_range('20150131', end='20171026')
df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx)
df.C = np.nan

df.loc['2015-01-31', 'C'] = df.loc['2015-01-31', 'D']

%%timeit
for i in df.loc['2015-02-01':].index.date:
    df.loc[i, 'C'] = df.loc[(i - pd.DateOffset(days=1)).date(), 'C'] * df.loc[i, 'A'] + df.loc[i, 'B']

Döngü başına 3,2 s ± 114 ms (ortalama ± std. Sapma 7 çalıştırma, her biri 1 döngü)

data = np.random.random(size=(1000, 4))
idx = pd.date_range('20150131', end='20171026')
df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx)
df.C = np.nan

def calculate(mul, add):
    global value
    value = value * mul + add
    return value

value = df.loc['2015-01-31', 'D']
df.loc['2015-01-31', 'C'] = value

%%timeit
df.loc['2015-02-01':, 'C'] = df.loc['2015-02-01':].apply(lambda row: calculate(*row[['A', 'B']]), axis=1)

Döngü başına 1,82 s ± 64,4 ms (ortalama ± std. Sapma 7 çalıştırma, her biri 1 döngü)

Yani ortalamada 0,57 kat daha hızlı.


0

Genel olarak, açık bir döngüden kaçınmanın anahtarı, rowindex-1 == rowindex üzerinde veri çerçevesinin 2 örneğini birleştirmek (birleştirmek) olacaktır.

Daha sonra, df.apply () işlevini yapabileceğiniz, r ve r-1 satırlarını içeren büyük bir veri çerçeveniz olur.

Bununla birlikte, büyük veri kümesi oluşturmanın ek yükü, paralel işlemenin faydalarını dengeleyebilir ...

HTH Martin

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.