Tôi có một DataFrame gấu trúc có dạng:
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
Những gì tôi đang cố gắng làm là điền vào sequence_no
mỗi id
/ start_time
combo bị thiếu . Ví dụ: id
/ start_time
ghép đôi 71
và 2018-10-17 20:12:43+00:00
, bị thiếu Sequ_no 114430. Đối với mỗi Sequ_no bị thiếu, tôi cũng cần trung bình / nội suy value
giá trị cột bị thiếu . Vì vậy, việc xử lý cuối cùng của dữ liệu trên sẽ có kết quả như sau:
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
( **
được thêm vào bên phải của các hàng mới được chèn để dễ đọc hơn)
Giải pháp ban đầu của tôi để thực hiện điều này phụ thuộc rất nhiều vào các vòng lặp Python trên một bảng dữ liệu lớn, vì vậy nó có vẻ như là nơi lý tưởng để numpy và gấu trúc tỏa sáng. Dựa vào các câu trả lời SO như Pandas: tạo các hàng để lấp đầy các khoảng trống số , tôi đã nghĩ ra:
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()
Đầu ra là chính xác, nhưng nó chạy ở tốc độ gần như chính xác như giải pháp rất nhiều python-loops của tôi. Tôi chắc chắn có những nơi tôi có thể cắt ra một vài bước, nhưng phần chậm nhất trong thử nghiệm của tôi dường như là reindex
. Cho rằng dữ liệu trong thế giới thực bao gồm gần một triệu hàng (được vận hành thường xuyên), có cách nào rõ ràng để đạt được một số lợi thế về hiệu suất so với những gì tôi đã viết không? Bất kỳ cách nào tôi có thể tăng tốc độ chuyển đổi này?
Cập nhật ngày 9/12/2019
Kết hợp giải pháp hợp nhất từ câu trả lời này với cấu trúc ban đầu của khung dữ liệu mở rộng mang lại kết quả nhanh nhất cho đến nay, khi được thử nghiệm trên một tập dữ liệu đủ lớn:
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
nhanh hơn đáng kể so vớireindex
, nhưng hóa raexplode
là rất chậm trên các tập dữ liệu lớn hơn. Khi kết hợp việc hợp nhất của bạn với cấu trúc ban đầu của bộ dữ liệu mở rộng, chúng tôi sẽ triển khai nhanh nhất cho đến nay (xem bản cập nhật