Pandas: Menghitung sejumlah nilai yang hilang sebelum/sesudah serangkaian nilai yang tersedia

Katakanlah saya memiliki rangkaian waktu di mana saya biasanya memiliki data yang tersedia untuk rentang tahun tertentu yang berkelanjutan, tetapi nilai yang hilang sebelum dan sesudah rentang tersebut, seperti ini:

df = pd.DataFrame({'year': ["2000","2001","2002", "2003","2004", "2005","2006", "2007"], 'cakes eaten': [np.nan, np.nan, np.nan, 3, 4, 5, np.nan, np.nan]})
print(df)

   year  cakes eaten
0  2000          NaN
1  2001          NaN
2  2002          NaN
3  2003          3.0
4  2004          4.0
5  2005          5.0
6  2006          NaN
7  2007          NaN

Apakah ada cara untuk mengisi (sejumlah) nilai yang hilang berdasarkan tren yang terlihat pada nilai yang tersedia?

Katakanlah saya ingin mengisi maksimal 2 nilai di setiap arah, hasilnya akan terlihat seperti ini:

   year  cakes eaten
0  2000          NaN
1  2001          1.0
2  2002          2.0
3  2003          3.0
4  2004          4.0
5  2005          5.0
6  2006          6.0
7  2007          7.0

Juga: apakah ada cara untuk memastikan bahwa imputasi ini hanya dilakukan ketika nilai yang tersedia mencukupi, misalnya saya hanya ingin mengisi maksimal 2 nilai di setiap arah jika setidaknya ada 3 nilai yang tersedia (atau dalam istilah yang lebih umum, isi n hanya jika n + m tersedia)?


person Christian O.    schedule 29.12.2020    source sumber
comment
Bagaimana Anda mengidentifikasi tren yang terlihat pada nilai yang tersedia?   -  person Dani Mesejo    schedule 29.12.2020


Jawaban (2)


Saya akan menggunakan interpolasi() yang disebutkan. Ada berbagai metode yang dapat Anda gunakan yang akan menghasilkan hasil berbeda. Saya menggunakan metode krogh untuk mendapatkan garis tren linier. limit_direction='both' diperlukan untuk mengisi tren di kedua arah:

test_dict  = {'col': [np.nan, np.nan,np.nan, np.nan, np.nan, 4, 5, 6 ,np.nan]}
df = pd.DataFrame(test_dict)
df['trend'] = df['col'].interpolate(method='krogh', limit_direction='both')

    col trend
0   NaN -1.0
1   NaN 0.0
2   NaN 1.0
3   NaN 2.0
4   NaN 3.0
5   4.0 4.0
6   5.0 5.0
7   6.0 6.0
8   NaN 7.0

Setelah selesai, Anda dapat menghapus nilai tren below 0 yang tidak diperlukan.

person olv1do    schedule 29.12.2020
comment
Terima kasih telah mengarahkan saya kembali ke fungsi interpolasi, sepertinya fungsi ini memang dapat melakukan apa yang saya inginkan. Krogh bekerja dengan sangat baik untuk contoh yang saya posting di atas, namun menghasilkan beberapa nilai yang sangat aneh jika trennya tidak linier sempurna. Namun, saya menemukan bahwa metode spline dengan order = 2 bekerja jauh lebih baik - person Christian O.; 29.12.2020

Terima kasih kepada @olv1do karena telah menunjukkan kepada saya bahwa interpolasi( ) melakukan apa yang saya inginkan.

Menggunakan interpolasi dan .first_valid_index dan .last_valid_index memungkinkan untuk mengimplementasikan perilaku yang diinginkan:

#impute n values in both directions if at least m values are available
def interpolate(data, n, m):
  first_valid = data['cakes eaten'].first_valid_index()
  last_valid = data['cakes eaten'].last_valid_index()

  if(abs(first_valid - last_valid) + 1 >= m):
    data['imputed'] = data['cakes eaten'].interpolate(method='spline',order = 1, limit_direction='both', limit = n)
  return data

Misalnya dari pertanyaan:

df = pd.DataFrame({'year': ["2000","2001","2002", "2003","2004", "2005","2006", "2007"], 'cakes eaten': [np.nan, np.nan, np.nan, 3, 4, 5, np.nan, np.nan]})
interpolate(df, 2,3)

year    cakes eaten     imputed
0   2000    NaN     NaN
1   2001    NaN     1.0
2   2002    NaN     2.0
3   2003    3.0     3.0
4   2004    4.0     4.0
5   2005    5.0     5.0
6   2006    NaN     6.0
7   2007    NaN     7.0

Tidak melakukan apa pun jika nilai yang tersedia kurang dari m:

df = pd.DataFrame({'year': ["2000","2001","2002", "2003","2004", "2005","2006", "2007"], 'cakes eaten': [np.nan, np.nan, np.nan, 3, 4,  np.nan, np.nan, np.nan]})
interpolate(df, 2,3)

    year    cakes eaten
0   2000    NaN
1   2001    NaN
2   2002    NaN
3   2003    3.0
4   2004    4.0
5   2005    NaN
6   2006    NaN
7   2007    NaN

Selain itu, metode spline juga berfungsi dengan baik jika nilainya tidak linier sempurna seperti pada contoh saya:

df = pd.DataFrame({'year': ["2000","2001","2002", "2003","2004", "2005","2006", "2007"], 'cakes eaten': [np.nan, np.nan, 1, 4, 2,  3, np.nan, np.nan]})
interpolate(df, 1,4)

    year    cakes eaten     imputed
0   2000    NaN     NaN
1   2001    NaN     1.381040
2   2002    1.0     1.000000
3   2003    4.0     4.000000
4   2004    2.0     2.000000
5   2005    3.0     3.000000
6   2006    NaN     3.433167
7   2007    NaN     NaN
person Christian O.    schedule 29.12.2020