La restituzione di un elenco da apply
è un'operazione pericolosa in quanto non è garantito che l'oggetto risultante sia una serie o un DataFrame. E in alcuni casi potrebbero essere sollevate eccezioni. Vediamo un semplice esempio:
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
Esistono tre possibili risultati con la restituzione di un elenco da apply
1) Se la lunghezza dell'elenco restituito non è uguale al numero di colonne, viene restituita una serie di elenchi.
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) Quando la lunghezza dell'elenco restituito è uguale al numero di colonne, viene restituito un DataFrame e ogni colonna ottiene il valore corrispondente nell'elenco.
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) Se la lunghezza dell'elenco restituito è uguale al numero di colonne per la prima riga ma ha almeno una riga in cui l'elenco ha un numero di elementi diverso rispetto al numero di colonne, viene generato un 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)
Rispondere al problema senza applicare
L'uso apply
con axis = 1 è molto lento. È possibile ottenere prestazioni molto migliori (soprattutto su set di dati più grandi) con metodi iterativi di base.
Crea frame di dati più grandi
df1 = df.sample(100000, replace=True).reset_index(drop=True)
Tempi
# 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 risponde
%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)