แพนด้า DataFrame MultiIndex ที่ช้า


13

ฉันมี DataFrame ของแบบฟอร์มแพนด้า:

                       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

สิ่งที่ฉันพยายามทำคือกรอกข้อมูลที่ขาดหายไปsequence_no ต่อ id / start_timeคอมโบ ตัวอย่างเช่น, id/ start_timepairing of 71และ2018-10-17 20:12:43+00:00, หายไป sequence_no 114430, สำหรับลำดับที่หายไปที่เพิ่มเข้ามาแต่ละอัน, ฉันต้องการค่าเฉลี่ย / สอดแทรกvalueค่าคอลัมน์ที่หายไปด้วย ดังนั้นการประมวลผลขั้นสุดท้ายของข้อมูลด้านบนจะเป็นดังนี้:

                       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

( **เพิ่มทางด้านขวาของแถวที่แทรกใหม่เพื่อให้อ่านง่ายขึ้น)

โซลูชันดั้งเดิมของฉันในการทำเช่นนี้อาศัย Python เป็นวงกว้างในตารางข้อมูลขนาดใหญ่ดังนั้นจึงดูเหมือนว่าเป็นสถานที่ที่เหมาะสำหรับผู้ที่ชอบถ่ายภาพและหมีแพนด้า การตอบคำถาม SO เช่นPandas: สร้างแถวเพื่อเติมช่องว่างที่เป็นตัวเลขฉันจึงได้:

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()

เอาท์พุทถูกต้อง แต่มันทำงานด้วยความเร็วเกือบเท่ากันกับทางออกมากมายของ python-loops ของฉัน ฉันแน่ใจว่ามีสถานที่ที่ฉันสามารถตัดออกไม่กี่ขั้นตอน reindexแต่ส่วนที่ช้าที่สุดในการทดสอบของฉันที่ดูเหมือนจะเป็น เนื่องจากข้อมูลโลกแห่งความจริงประกอบด้วยแถวเกือบล้านแถว (ดำเนินการบ่อยครั้ง) มีวิธีใดที่ชัดเจนในการเพิ่มความได้เปรียบด้านประสิทธิภาพสำหรับสิ่งที่ฉันเขียนไปแล้ว? วิธีใดบ้างที่ฉันสามารถเร่งความเร็วการเปลี่ยนแปลงนี้

อัปเดตเมื่อวันที่ 9/12/2562

การรวมโซลูชันการผสานจากคำตอบนี้เข้ากับการสร้างแบบดั้งเดิมของดาต้าเฟรมที่ขยายออกมานั้นให้ผลลัพธ์ที่เร็วที่สุดเมื่อทำการทดสอบบนชุดข้อมูลที่มีขนาดใหญ่พอ:

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()

คำตอบ:


8

ใช้mergeแทนreindexอาจจะเร็วขึ้น นอกจากนี้การใช้แผนที่แทนความเข้าใจในรายการอาจทำได้เช่นกัน

# 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)])
# map with range to create the sequence number rnage
ranges['sequence_no'] = list(map(lambda x,y: range(x,y), ranges.pop('sequence_min'), ranges.pop('sequence_max')+1))
# explode you DataFrame
new_df = ranges.explode('sequence_no')
# merge new_df and df
merge = new_df.reset_index().merge(df, on=['start_time', 'id', 'sequence_no'], how='left')
# interpolate and assign values 
merge['value'] = merge['value'].interpolate()

                   start_time  id sequence_no  value
0   2018-10-17 20:12:43+00:00  71      114428    3.0
1   2018-10-17 20:12:43+00:00  71      114429    3.0
2   2018-10-17 20:12:43+00:00  71      114430   41.0
3   2018-10-17 20:12:43+00:00  71      114431   79.0
4   2019-11-06 00:51:14+00:00  71      216009  100.0
5   2019-11-06 00:51:14+00:00  71      216010  125.0
6   2019-11-06 00:51:14+00:00  71      216011  150.0
7   2019-11-06 00:51:14+00:00  71      216012  165.0
8   2019-11-06 00:51:14+00:00  71      216013  180.0
9   2019-12-01 00:51:14+00:00  92      114430   19.0
10  2019-12-01 00:51:14+00:00  92      114431   39.0
11  2019-12-01 00:51:14+00:00  92      114432   59.0
12  2019-12-01 00:51:14+00:00  92      114433   79.0
13  2019-12-01 00:51:14+00:00  92      114434  100.0

นี่เป็นกรณีที่น่าสนใจของ "ก้าวไปข้างหน้าถอยหลังหนึ่งก้าว" คุณถูกต้องแล้วว่าmergeมันเร็วกว่าอย่างreindexมาก แต่ปรากฎว่าexplodeชุดข้อมูลที่ใหญ่กว่านั้นช้ามาก เมื่อรวมการผสานของคุณเข้ากับการสร้างชุดข้อมูลแบบขยายเราจะใช้งานได้เร็วที่สุด (ดูการอัปเดตคำถามใน
9/12/2562

1
@ MBrizzle นอกจากนี้ฉันควรทราบว่าการเพิ่มพารามิเตอร์copy=Falseในการผสานควรเพิ่มความเร็วให้เร็วขึ้นเล็กน้อยเช่นเดียวกับคุณจะหลีกเลี่ยงการคัดลอกข้อมูลที่ไม่จำเป็น merge = expanded.merge(df, on=['start_time', 'id', 'sequence_no'], how='left', copy=False)
Yo_Chris

3

mergeโซลูชันที่สั้นกว่า:

df.groupby(['start_time', 'id'])['sequence_no']\
.apply(lambda x: np.arange(x.min(), x.max() + 1))\
.explode().reset_index()\
.merge(df, on=['start_time', 'id', 'sequence_no'], how='left')\
.interpolate()

เอาท์พุท:

                   start_time  id sequence_no  value
0   2018-10-17 20:12:43+00:00  71      114428    3.0
1   2018-10-17 20:12:43+00:00  71      114429    3.0
2   2018-10-17 20:12:43+00:00  71      114430   41.0
3   2018-10-17 20:12:43+00:00  71      114431   79.0
4   2019-11-06 00:51:14+00:00  71      216009  100.0
5   2019-11-06 00:51:14+00:00  71      216010  125.0
6   2019-11-06 00:51:14+00:00  71      216011  150.0
7   2019-11-06 00:51:14+00:00  71      216012  165.0
8   2019-11-06 00:51:14+00:00  71      216013  180.0
9   2019-12-01 00:51:14+00:00  92      114430   19.0
10  2019-12-01 00:51:14+00:00  92      114431   39.0
11  2019-12-01 00:51:14+00:00  92      114432   59.0
12  2019-12-01 00:51:14+00:00  92      114433   79.0
13  2019-12-01 00:51:14+00:00  92      114434  100.0

1

โซลูชันอื่นที่reindexไม่ใช้explode:

result = (df.groupby(["id","start_time"])
          .apply(lambda d: d.set_index("sequence_no")
          .reindex(range(min(d["sequence_no"]),max(d["sequence_no"])+1)))
          .drop(["id","start_time"],axis=1).reset_index()
          .interpolate())

print (result)

#
    id                 start_time  sequence_no  value
0   71  2018-10-17 20:12:43+00:00       114428    3.0
1   71  2018-10-17 20:12:43+00:00       114429    3.0
2   71  2018-10-17 20:12:43+00:00       114430   41.0
3   71  2018-10-17 20:12:43+00:00       114431   79.0
4   71  2019-11-06 00:51:14+00:00       216009  100.0
5   71  2019-11-06 00:51:14+00:00       216010  125.0
6   71  2019-11-06 00:51:14+00:00       216011  150.0
7   71  2019-11-06 00:51:14+00:00       216012  165.0
8   71  2019-11-06 00:51:14+00:00       216013  180.0
9   92  2019-12-01 00:51:14+00:00       114430   19.0
10  92  2019-12-01 00:51:14+00:00       114431   39.0
11  92  2019-12-01 00:51:14+00:00       114432   59.0
12  92  2019-12-01 00:51:14+00:00       114433   79.0
13  92  2019-12-01 00:51:14+00:00       114434  100.0
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.