การส่งคืนรายการจากapply
เป็นการดำเนินการที่อันตรายเนื่องจากวัตถุที่เป็นผลลัพธ์ไม่รับประกันว่าจะเป็น Series หรือ DataFrame และอาจมีข้อยกเว้นในบางกรณี ลองมาดูตัวอย่างง่ายๆ:
df = pd.DataFrame(data=np.random.randint(0, 5, (5,3)),
columns=['a', 'b', 'c'])
df
a b c
0 4 0 0
1 2 0 1
2 2 2 2
3 1 2 2
4 3 0 0
มีสามผลลัพธ์ที่เป็นไปได้โดยส่งคืนรายการจาก apply
1)หากความยาวของรายการที่ส่งคืนไม่เท่ากับจำนวนคอลัมน์จะมีการส่งคืนชุดรายการ
df.apply(lambda x: list(range(2)), axis=1) # returns a Series
0 [0, 1]
1 [0, 1]
2 [0, 1]
3 [0, 1]
4 [0, 1]
dtype: object
2)เมื่อความยาวของรายการที่ส่งคืนเท่ากับจำนวนคอลัมน์แล้ว DataFrame จะถูกส่งกลับและแต่ละคอลัมน์จะได้รับค่าที่สอดคล้องกันในรายการ
df.apply(lambda x: list(range(3)), axis=1) # returns a DataFrame
a b c
0 0 1 2
1 0 1 2
2 0 1 2
3 0 1 2
4 0 1 2
3)หากความยาวของรายการที่ส่งคืนเท่ากับจำนวนคอลัมน์สำหรับแถวแรก แต่มีอย่างน้อยหนึ่งแถวที่รายการมีจำนวนองค์ประกอบที่แตกต่างจากจำนวนคอลัมน์ที่เพิ่มขึ้น ValueError
i = 0
def f(x):
global i
if i == 0:
i += 1
return list(range(3))
return list(range(4))
df.apply(f, axis=1)
ValueError: Shape of passed values is (5, 4), indices imply (5, 3)
ตอบปัญหาโดยไม่ต้องสมัคร
การใช้apply
กับ axis = 1 ช้ามาก เป็นไปได้ที่จะได้รับประสิทธิภาพที่ดีขึ้นมาก (โดยเฉพาะชุดข้อมูลขนาดใหญ่) ด้วยวิธีการทำซ้ำขั้นพื้นฐาน
สร้างดาต้าเฟรมที่ใหญ่ขึ้น
df1 = df.sample(100000, replace=True).reset_index(drop=True)
การกำหนดเวลา
# apply is slow with axis=1
%timeit df1.apply(lambda x: mylist[x['col_1']: x['col_2']+1], axis=1)
2.59 s ± 76.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
# zip - similar to @Thomas
%timeit [mylist[v1:v2+1] for v1, v2 in zip(df1.col_1, df1.col_2)]
29.5 ms ± 534 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
@ คำตอบโทมัส
%timeit list(map(get_sublist, df1['col_1'],df1['col_2']))
34 ms ± 459 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)