Trả về danh sách từ apply
là một hoạt động nguy hiểm vì đối tượng kết quả không được đảm bảo là Sê-ri hoặc Khung dữ liệu. Và ngoại lệ có thể được nêu ra trong một số trường hợp nhất định. Hãy đi qua một ví dụ đơn giản:
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
Có ba kết quả có thể xảy ra với việc trả về một danh sách từ apply
1) Nếu độ dài của danh sách được trả về không bằng số lượng cột, thì một loạt danh sách được trả về.
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) Khi độ dài của danh sách được trả về bằng số lượng cột thì DataFrame được trả về và mỗi cột nhận giá trị tương ứng trong danh sách.
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) Nếu độ dài của danh sách được trả về bằng số lượng cột cho hàng đầu tiên nhưng có ít nhất một hàng trong đó danh sách có số phần tử khác với số cột mà ValueError được nâng lên.
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)
Trả lời vấn đề mà không áp dụng
Sử dụng apply
với trục = 1 là rất chậm. Có thể có được hiệu suất tốt hơn nhiều (đặc biệt là trên các bộ dữ liệu lớn hơn) với các phương pháp lặp cơ bản.
Tạo khung dữ liệu lớn hơn
df1 = df.sample(100000, replace=True).reset_index(drop=True)
Thời gian
# 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)
@Thomas trả lời
%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)