Aplicar una función de pares en una serie de pandas.

Tengo una serie de pandas cuyos elementos constituyen frozensets:

data = {0: frozenset({'apple', 'banana'}), 1: frozenset({'apple', 'orange'}), 2: frozenset({'banana'}), 3: frozenset({'kumquat', 'orange'}), 4: frozenset({'orange'}), 5: frozenset({'orange', 'pear'}), 6: frozenset({'orange', 'pear'}), 7: frozenset({'apple', 'banana', 'pear'}), 8: frozenset({'banana', 'persimmon'}), 9: frozenset({'apple'}), 10: frozenset({'banana'}), 11: frozenset({'apple'})} tokens = pd.Series(data); tokens 0 (apple, banana) 1 (orange, apple) 2 (banana) 3 (orange, kumquat) 4 (orange) 5 (orange, pear) 6 (orange, pear) 7 (apple, banana, pear) 8 (persimmon, banana) 9 (apple) 10 (banana) 11 (apple) Name: Tokens, dtype: object 

Quiero aplicar una función por pares. Por ejemplo, tokens.diff me da la diferencia establecida entre filas consecutivas:

 0 NaN 1 (orange) 2 (banana) 3 (orange, kumquat) 4 () 5 (pear) 6 () 7 (apple, banana) 8 (persimmon) 9 (apple) 10 (banana) 11 (apple) Name: Tokens, dtype: object 

Me gustaría lo mismo, pero en lugar de establecer la diferencia, quiero una unión establecida en filas consecutivas. Por lo tanto, me gustaría idealmente:

 0 NaN 1 (orange, apple, banana) 2 (banana, orange, apply) 3 (orange, kumquat, banana) 4 (orange, kumquat) ... 

¿Cómo puedo lograr esto con Pandas? Sé que puedo hacer esto con zip y una lista comp, pero espero que haya una mejor manera.

Par de maneras

Opción 1] lista de comprensión

 In [3631]: pd.Series([x[0].union(x[1]) for x in zip(tokens, tokens.shift(-1).fillna(''))], index=tokens.index) Out[3631]: 0 (orange, banana, apple) 1 (orange, apple, banana) 2 (orange, kumquat, banana) 3 (orange, kumquat) 4 (orange, pear) 5 (orange, pear) 6 (orange, pear, banana, apple) 7 (persimmon, pear, banana, apple) 8 (apple, persimmon, banana) 9 (apple, banana) 10 (banana, apple) 11 (apple) dtype: object 

Opción 2] map

 In [3632]: pd.Series(map(lambda x: x[0].union(x[1]), zip(tokens, tokens.shift(-1).fillna(''))), index=tokens.index) Out[3632]: 0 (orange, banana, apple) 1 (orange, apple, banana) 2 (orange, kumquat, banana) 3 (orange, kumquat) 4 (orange, pear) 5 (orange, pear) 6 (orange, pear, banana, apple) 7 (persimmon, pear, banana, apple) 8 (apple, persimmon, banana) 9 (apple, banana) 10 (banana, apple) 11 (apple) dtype: object 

Opción 3] Usar concat y apply

 In [3633]: pd.concat([tokens, tokens.shift(-1).fillna('')], axis=1).apply(lambda x: x[0].union(x[1]), axis=1) Out[3633]: 0 (orange, banana, apple) 1 (orange, apple, banana) 2 (orange, kumquat, banana) 3 (orange, kumquat) 4 (orange, pear) 5 (orange, pear) 6 (orange, pear, banana, apple) 7 (persimmon, pear, banana, apple) 8 (apple, persimmon, banana) 9 (apple, banana) 10 (banana, apple) 11 (apple) dtype: object 

Tiempos

 In [3647]: tokens.shape Out[3647]: (60000L,) In [3648]: %timeit pd.Series([x[0].union(x[1]) for x in zip(tokens, tokens.shift(-1).fillna(''))], index=tokens.index) 10 loops, best of 3: 35 ms per loop In [3649]: %timeit pd.Series(map(lambda x: x[0].union(x[1]), zip(tokens, tokens.shift(-1).fillna(''))), index=tokens.index) 10 loops, best of 3: 40.9 ms per loop In [3650]: %timeit pd.concat([tokens, tokens.shift(-1).fillna('')], axis=1).apply(lambda x: x[0].union(x[1]), axis=1) 1 loop, best of 3: 2.2 s per loop 

Sin relación y por causa de un número en diff

 In [3653]: %timeit tokens.diff() 10 loops, best of 3: 10.8 ms per loop