簡體   English   中英

在 Pandas 時間序列 dataframe 中使用自定義條件填充缺失數據

[英]Filling missing data using a custom condition in a Pandas time series dataframe

下面是我的dataframe的一部分,它有很多缺失值。

            A                                       B
S           a       b       c       d       e       a       b       c       d       e
date                                        
2020-10-15  1.0     2.0     NaN     NaN     NaN     10.0    11.0    NaN     NaN     NaN
2020-10-16  NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN
2020-10-17  NaN     NaN     NaN     4.0     NaN     NaN     NaN     NaN     13.0    NaN
2020-10-18  NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN
2020-10-19  NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN     NaN
2020-10-20  4.0     6.0     4.0     1.0     9.0     10.0    2.0     13.0    4.0     13.0

我想使用specific backward fill condition替換每列中的NANs

例如,在 (A,a) 列中,缺失值出現在 16 日、17 日、18 日和 19 日。 下一個值是 '4' 對 20。 我希望這個值(列中的下一個非缺失值)分布在所有這些日期中,包括 20 日,並逐漸增加 10% 的值。 也就是說,(A,a) 列在 16 日、17 日、18 日、19 日和 20 日的日期大約為 .655、.720、.793、.872 和 .96。 這應該是跨行所有缺失值的所有列的方法。

我嘗試使用 bfill() function 但無法理解如何將所需的公式作為選項合並。

我已經檢查了鏈接Pandas: Filling missing values in time series forward using a formulastackoverflow上的一些其他鏈接。 這有點相似,但在我的情況下,給定列中的 NAN 數量本質上是可變的,並且跨越多行。 將 (A,a) 列與 (A,d) 列或 (B,d) 列進行比較。 鑒於此,我發現很難采用解決方案來解決我的問題。

感謝任何輸入。

這是一種完全矢量化的方法。 它非常高效且快速:在 1000 x 1000 矩陣上為 130 毫秒。 這是一個展示使用numpy的有趣技術的好機會。

首先,讓我們深入了解一下需求,特別是每個單元格需要的確切值。

給出的示例是[nan, nan, nan, nan, 4.0] --> [.66, .72, .79, .87, .96] ,這被解釋為“10% 的遞增值”(這樣總數就是“傳播價值”: 4.0 )。

這是一個幾何級數,速率為r = 1 + 0.1 : [r^1, r^2, r^3, ...]然后歸一化為總和為 1。例如:

r = 1.1
a = 4.0
n = 5
q = np.cumprod(np.repeat(r, n))
a * q / q.sum()
# array([0.65518992, 0.72070892, 0.79277981, 0.87205779, 0.95926357])

我們想做一個直接計算(避免調用 Python 函數和顯式循環,這會慢得多),所以我們需要以封閉形式表示歸一化因子q.sum() 這是一個公認的數量,並且是:

概括地說,我們需要 3 個量來計算每個單元格的值:

  • a : 要分配的值
  • i : 運行指數 (0.. n-1)
  • n : 運行長度
  • 那么,值為v = a * r**i * (r - 1) / (r**n - 1)

為了說明 OP 示例中的第一列,其中輸入為: [1, nan, nan, nan, nan, 4] ,我們希望:

  • a = [1, 4, 4, 4, 4, 4]
  • i = [0, 0, 1, 2, 3, 4]
  • n = [1, 5, 5, 5, 5, 5]
  • 那么,值v將是(四舍五入到小數點后 2 位): [1. , 0.66, 0.72, 0.79, 0.87, 0.96] [1. , 0.66, 0.72, 0.79, 0.87, 0.96]

現在是我們 go 關於將這三個數量作為 numpy arrays 的部分。

a是最簡單的,只是df.bfill().values 但是對於in ,我們必須做一些工作,首先將值分配給 numpy 數組:

z = df.values
nrows, ncols = z.shape

對於i ,我們從NaN的累積計數開始,當值不是NaN時重置。 這受到“沒有迭代的 NumPy 中的累積計數”的SO 答案的強烈啟發。 但是我們是為二維數組做的,我們還想添加第一行 0,並丟棄最后一行以滿足我們的需求:

def rcount(z):
    na = np.isnan(z)
    without_reset = na.cumsum(axis=0)
    reset_at = ~na
    overcount = np.maximum.accumulate(without_reset * reset_at)
    result = without_reset - overcount
    return result

i = np.vstack((np.zeros(ncols, dtype=bool), rcount(z)))[:-1]

對於n ,我們需要自己做一些跳舞,使用 numpy 的第一原則(如果有時間我會分解步驟):

runlen = np.diff(np.hstack((-1, np.flatnonzero(~np.isnan(np.vstack((z, np.ones(ncols))).T)))))
n = np.reshape(np.repeat(runlen, runlen), (nrows + 1, ncols), order='F')[:-1]

所以,把它們放在一起:

def spread_bfill(df, r=1.1):
    z = df.values
    nrows, ncols = z.shape

    a = df.bfill().values
    i = np.vstack((np.zeros(ncols, dtype=bool), rcount(z)))[:-1]
    runlen = np.diff(np.hstack((-1, np.flatnonzero(~np.isnan(np.vstack((z, np.ones(ncols))).T)))))
    n = np.reshape(np.repeat(runlen, runlen), (nrows + 1, ncols), order='F')[:-1]
    v = a * r**i * (r - 1) / (r**n - 1)
    return pd.DataFrame(v, columns=df.columns, index=df.index)

根據您的示例數據,我們得到:

>>> spread_bfill(df).round(2)  # round(2) for printing purposes
               A                              B                         
               a     b     c     d     e      a      b     c     d     e
S                                                                       
2020-10-15  1.00  2.00  0.52  1.21  1.17  10.00  11.00  1.68  3.93  1.68
2020-10-16  0.66  0.98  0.57  1.33  1.28   1.64   0.33  1.85  4.32  1.85
2020-10-17  0.72  1.08  0.63  1.46  1.41   1.80   0.36  2.04  4.75  2.04
2020-10-18  0.79  1.19  0.69  0.30  1.55   1.98   0.40  2.24  1.21  2.24
2020-10-19  0.87  1.31  0.76  0.33  1.71   2.18   0.44  2.47  1.33  2.47
2020-10-20  0.96  1.44  0.83  0.37  1.88   2.40   0.48  2.71  1.46  2.71

為了檢查,讓我們看一下該示例中的 3 個數量中的每一個:

>>> a
[[ 1  2  4  4  9 10 11 13 13 13]
 [ 4  6  4  4  9 10  2 13 13 13]
 [ 4  6  4  4  9 10  2 13 13 13]
 [ 4  6  4  1  9 10  2 13  4 13]
 [ 4  6  4  1  9 10  2 13  4 13]
 [ 4  6  4  1  9 10  2 13  4 13]]

>>> i
[[0 0 0 0 0 0 0 0 0 0]
 [0 0 1 1 1 0 0 1 1 1]
 [1 1 2 2 2 1 1 2 2 2]
 [2 2 3 0 3 2 2 3 0 3]
 [3 3 4 1 4 3 3 4 1 4]
 [4 4 5 2 5 4 4 5 2 5]]

>>> n
[[1 1 6 3 6 1 1 6 3 6]
 [5 5 6 3 6 5 5 6 3 6]
 [5 5 6 3 6 5 5 6 3 6]
 [5 5 6 3 6 5 5 6 3 6]
 [5 5 6 3 6 5 5 6 3 6]
 [5 5 6 3 6 5 5 6 3 6]]

這是最后一個示例,用於說明如果列以 1 個或多個NaN結尾(它們仍然是NaN )會發生什么:

np.random.seed(10)
a = np.random.randint(0, 10, (6, 6)).astype(float)
a *= np.random.choice([1.0, np.nan], a.shape, p=[.3, .7])
df = pd.DataFrame(a)
>>> df
    0    1    2    3    4    5
0 NaN  NaN  NaN  NaN  NaN  0.0
1 NaN  NaN  9.0  NaN  8.0  NaN
2 NaN  NaN  NaN  NaN  NaN  NaN
3 NaN  8.0  4.0  NaN  NaN  NaN
4 NaN  NaN  NaN  6.0  9.0  NaN
5 NaN  NaN  2.0  NaN  7.0  8.0

然后:

>>> spread_bfill(df).round(2)  # round(2) for printing
    0     1     2     3     4     5
0 NaN  1.72  4.29  0.98  3.81  0.00
1 NaN  1.90  4.71  1.08  4.19  1.31
2 NaN  2.09  1.90  1.19  2.72  1.44
3 NaN  2.29  2.10  1.31  2.99  1.59
4 NaN   NaN  0.95  1.44  3.29  1.74
5 NaN   NaN  1.05   NaN  7.00  1.92

速度

a = np.random.randint(0, 10, (1000, 1000)).astype(float)
a *= np.random.choice([1.0, np.nan], a.shape, p=[.3, .7])
df = pd.DataFrame(a)

%timeit spread_bfill(df)
# 130 ms ± 142 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

初始數據:

>>> df
              A                         B
              a    b    c    d    e     a     b     c     d     e
date
2020-10-15  1.0  2.0  NaN  NaN  NaN  10.0  11.0   NaN   NaN   NaN
2020-10-16  NaN  NaN  NaN  NaN  NaN   NaN   NaN   NaN   NaN   NaN
2020-10-17  NaN  NaN  NaN  4.0  NaN   NaN   NaN   NaN  13.0   NaN
2020-10-18  NaN  NaN  NaN  NaN  NaN   NaN   NaN   NaN   NaN   NaN
2020-10-19  NaN  NaN  NaN  NaN  NaN   NaN   NaN   NaN   NaN   NaN
2020-10-20  4.0  6.0  4.0  1.0  9.0  10.0   2.0  13.0   4.0  13.0

定義幾何序列:

def geomseq(seq):
    q = 1.1
    n = len(seq)
    S = seq.max()
    Uo = S * (1-q) / (1-q**n)
    Un = [Uo * q**i for i in range(0, n)]
    return Un

TL;博士

>>> df.unstack().groupby(df.unstack().sort_index(ascending=False).notna().cumsum().sort_index()).transform(geomseq).unstack(level=[0, 1])
                   A                                                  B
                   a         b         c         d         e          a          b         c         d         e
date
2020-10-15  1.000000  2.000000  0.518430  1.208459  1.166466  10.000000  11.000000  1.684896  3.927492  1.684896
2020-10-16  0.655190  0.982785  0.570272  1.329305  1.283113   1.637975   0.327595  1.853386  4.320242  1.853386
2020-10-17  0.720709  1.081063  0.627300  1.462236  1.411424   1.801772   0.360354  2.038724  4.752266  2.038724
2020-10-18  0.792780  1.189170  0.690030  0.302115  1.552567   1.981950   0.396390  2.242597  1.208459  2.242597
2020-10-19  0.872058  1.308087  0.759033  0.332326  1.707823   2.180144   0.436029  2.466856  1.329305  2.466856
2020-10-20  0.959264  1.438895  0.834936  0.365559  1.878606   2.398159   0.479632  2.713542  1.462236  2.713542

細節

將您的dataframe轉換為series

>>> sr = df.unstack()
>>> sr.head(10)
      date
A  a  2020-10-15    1.0
      2020-10-16    NaN  # <= group X (final value: .655)
      2020-10-17    NaN  # <= group X (final value: .720)
      2020-10-18    NaN  # <= group X (final value: .793)
      2020-10-19    NaN  # <= group X (final value: .872)
      2020-10-20    4.0  # <= group X (final value: .960)
   b  2020-10-15    2.0
      2020-10-16    NaN
      2020-10-17    NaN
      2020-10-18    NaN
dtype: float64

現在您可以建立組:

>>> groups = sr.sort_index(ascending=False).notna().cumsum().sort_index()
>>> groups.head(10)
      date
A  a  2020-10-15    16
      2020-10-16    15  # <= group X15
      2020-10-17    15  # <= group X15
      2020-10-18    15  # <= group X15
      2020-10-19    15  # <= group X15
      2020-10-20    15  # <= group X15
   b  2020-10-15    14
      2020-10-16    13
      2020-10-17    13
      2020-10-18    13
dtype: int64

應用幾何級數:

>>> sr = sr.groupby(groups).transform(geomseq)
>>> sr.head(10)
      date
A  a  2020-10-15    1.000000
      2020-10-16    0.655190  # <= group X15
      2020-10-17    0.720709  # <= group X15
      2020-10-18    0.792780  # <= group X15
      2020-10-19    0.872058  # <= group X15
      2020-10-20    0.959264  # <= group X15
   b  2020-10-15    2.000000
      2020-10-16    0.982785
      2020-10-17    1.081063
      2020-10-18    1.189170
dtype: float64

最后,根據您最初的dataframe重塑series

>>> df = sr.unstack(level=[0, 1])
>>> df
                   A                                                  B
                   a         b         c         d         e          a          b         c         d         e
date
2020-10-15  1.000000  2.000000  0.518430  1.208459  1.166466  10.000000  11.000000  1.684896  3.927492  1.684896
2020-10-16  0.655190  0.982785  0.570272  1.329305  1.283113   1.637975   0.327595  1.853386  4.320242  1.853386
2020-10-17  0.720709  1.081063  0.627300  1.462236  1.411424   1.801772   0.360354  2.038724  4.752266  2.038724
2020-10-18  0.792780  1.189170  0.690030  0.302115  1.552567   1.981950   0.396390  2.242597  1.208459  2.242597
2020-10-19  0.872058  1.308087  0.759033  0.332326  1.707823   2.180144   0.436029  2.466856  1.329305  2.466856
2020-10-20  0.959264  1.438895  0.834936  0.365559  1.878606   2.398159   0.479632  2.713542  1.462236  2.713542

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM