Saya memiliki DataFrame panda dalam bentuk:
id start_time sequence_no value
0 71 2018-10-17 20:12:43+00:00 114428 3
1 71 2018-10-17 20:12:43+00:00 114429 3
2 71 2018-10-17 20:12:43+00:00 114431 79
3 71 2019-11-06 00:51:14+00:00 216009 100
4 71 2019-11-06 00:51:14+00:00 216011 150
5 71 2019-11-06 00:51:14+00:00 216013 180
6 92 2019-12-01 00:51:14+00:00 114430 19
7 92 2019-12-01 00:51:14+00:00 114433 79
8 92 2019-12-01 00:51:14+00:00 114434 100
Apa yang saya coba lakukan adalah mengisi sequence_no
per id
/ start_time
combo yang hilang . Sebagai contoh, id
/ start_time
pairing dari 71
dan 2018-10-17 20:12:43+00:00
, tidak ada sequence_no 114430. Untuk setiap tambahan yang hilang sequence_no, saya juga perlu rata-rata / interpolasi nilai value
kolom yang hilang . Jadi, pemrosesan akhir dari data di atas akan tampak seperti:
id start_time sequence_no value
0 71 2018-10-17 20:12:43+00:00 114428 3
1 71 2018-10-17 20:12:43+00:00 114429 3
2 71 2018-10-17 20:12:43+00:00 114430 41 **
3 71 2018-10-17 20:12:43+00:00 114431 79
4 71 2019-11-06 00:51:14+00:00 216009 100
5 71 2019-11-06 00:51:14+00:00 216010 125 **
6 71 2019-11-06 00:51:14+00:00 216011 150
7 71 2019-11-06 00:51:14+00:00 216012 165 **
8 71 2019-11-06 00:51:14+00:00 216013 180
9 92 2019-12-01 00:51:14+00:00 114430 19
10 92 2019-12-01 00:51:14+00:00 114431 39 **
11 92 2019-12-01 00:51:14+00:00 114432 59 **
12 92 2019-12-01 00:51:14+00:00 114433 79
13 92 2019-12-01 00:51:14+00:00 114434 100
( **
ditambahkan di sebelah kanan baris yang baru disisipkan untuk memudahkan pembacaan)
Solusi asli saya untuk melakukan ini sangat bergantung pada loop Python di atas meja besar data, jadi sepertinya tempat yang ideal untuk numpy dan panda bersinar. Bersandar pada jawaban SO seperti Pandas: buat baris untuk mengisi kesenjangan numerik , saya datang dengan:
import pandas as pd
import numpy as np
# Generate dummy data
df = pd.DataFrame([
(71, '2018-10-17 20:12:43+00:00', 114428, 3),
(71, '2018-10-17 20:12:43+00:00', 114429, 3),
(71, '2018-10-17 20:12:43+00:00', 114431, 79),
(71, '2019-11-06 00:51:14+00:00', 216009, 100),
(71, '2019-11-06 00:51:14+00:00', 216011, 150),
(71, '2019-11-06 00:51:14+00:00', 216013, 180),
(92, '2019-12-01 00:51:14+00:00', 114430, 19),
(92, '2019-12-01 00:51:14+00:00', 114433, 79),
(92, '2019-12-01 00:51:14+00:00', 114434, 100),
], columns=['id', 'start_time', 'sequence_no', 'value'])
# create a new DataFrame with the min/max `sequence_no` values for each `id`/`start_time` pairing
by_start = df.groupby(['start_time', 'id'])
ranges = by_start.agg(
sequence_min=('sequence_no', np.min), sequence_max=('sequence_no', np.max)
)
reset = ranges.reset_index()
mins = reset['sequence_min']
maxes = reset['sequence_max']
# Use those min/max values to generate a sequence with ALL values in that range
expanded = pd.DataFrame(dict(
start_time=reset['start_time'].repeat(maxes - mins + 1),
id=reset['id'].repeat(maxes - mins + 1),
sequence_no=np.concatenate([np.arange(mins, maxes + 1) for mins, maxes in zip(mins, maxes)])
))
# Use the above generated DataFrame as an index to generate the missing rows, then interpolate
expanded_index = pd.MultiIndex.from_frame(expanded)
df.set_index(
['start_time', 'id', 'sequence_no']
).reindex(expanded_index).interpolate()
Outputnya benar, tetapi berjalan pada kecepatan yang hampir sama persis dengan solusi banyak-python-loop saya. Saya yakin ada tempat saya bisa memotong beberapa langkah, tetapi bagian paling lambat dalam pengujian saya tampaknya adalah reindex
. Mengingat bahwa data dunia nyata terdiri dari hampir satu juta baris (sering dioperasikan), apakah ada cara yang jelas untuk mendapatkan beberapa keunggulan kinerja daripada apa yang telah saya tulis? Adakah cara saya dapat mempercepat transformasi ini?
Pembaruan 9/12/2019
Menggabungkan solusi gabungan dari jawaban ini dengan konstruksi asli dari kerangka data diperluas menghasilkan hasil tercepat sejauh ini, ketika diuji pada dataset yang cukup besar:
import pandas as pd
import numpy as np
# Generate dummy data
df = pd.DataFrame([
(71, '2018-10-17 20:12:43+00:00', 114428, 3),
(71, '2018-10-17 20:12:43+00:00', 114429, 3),
(71, '2018-10-17 20:12:43+00:00', 114431, 79),
(71, '2019-11-06 00:51:14+00:00', 216009, 100),
(71, '2019-11-06 00:51:14+00:00', 216011, 150),
(71, '2019-11-06 00:51:14+00:00', 216013, 180),
(92, '2019-12-01 00:51:14+00:00', 114430, 19),
(92, '2019-12-01 00:51:14+00:00', 114433, 79),
(92, '2019-12-01 00:51:14+00:00', 114434, 100),
], columns=['id', 'start_time', 'sequence_no', 'value'])
# create a ranges df with groupby and agg
ranges = df.groupby(['start_time', 'id'])['sequence_no'].agg([
('sequence_min', np.min), ('sequence_max', np.max)
])
reset = ranges.reset_index()
mins = reset['sequence_min']
maxes = reset['sequence_max']
# Use those min/max values to generate a sequence with ALL values in that range
expanded = pd.DataFrame(dict(
start_time=reset['start_time'].repeat(maxes - mins + 1),
id=reset['id'].repeat(maxes - mins + 1),
sequence_no=np.concatenate([np.arange(mins, maxes + 1) for mins, maxes in zip(mins, maxes)])
))
# merge expanded and df
merge = expanded.merge(df, on=['start_time', 'id', 'sequence_no'], how='left')
# interpolate and assign values
merge['value'] = merge['value'].interpolate()
merge
secara signifikan lebih cepat daripadareindex
, tetapi ternyataexplode
sangat lambat pada set data yang lebih besar. Saat menggabungkan penggabungan Anda dengan konstruksi asli dari dataset yang diperluas, kami mendapatkan implementasi tercepat sejauh ini (lihat pembaruan 9/12/2019 untuk pertanyaan)copy=False
ke penggabungan harus mempercepat sedikit dan Anda akan menghindari penyalinan data yang tidak perlu.merge = expanded.merge(df, on=['start_time', 'id', 'sequence_no'], how='left', copy=False)
Versi
merge
solusi yang lebih pendek :Keluaran:
sumber
Solusi lain dengan
reindex
tanpa menggunakanexplode
:sumber