Una solución alternativa sería utilizar la herramienta de flujo de trabajo dask. Aunque no es tan divertido sintácticamente como ...
var
| do this
| then do that
... todavía permite que su variable fluya hacia abajo en la cadena y el uso de dask brinda el beneficio adicional de la paralelización siempre que sea posible.
Así es como uso dask para lograr un patrón de cadena de tuberías:
import dask
def a(foo):
return foo + 1
def b(foo):
return foo / 2
def c(foo,bar):
return foo + bar
workflow = {'a_task':(a,1),
'b_task':(b,'a_task',),
'c_task':(c,99,'b_task'),}
dask.get(workflow,'c_task')
Después de haber trabajado con elixir, quería usar el patrón de tuberías en Python. Este no es exactamente el mismo patrón, pero es similar y, como dije, viene con beneficios adicionales de la paralelización; Si le dice a dask que obtenga una tarea en su flujo de trabajo que no depende de que otras se ejecuten primero, se ejecutarán en paralelo.
Si desea una sintaxis más sencilla, puede envolverla en algo que se encargue de nombrar las tareas por usted. Por supuesto, en esta situación, necesitaría todas las funciones para tomar la tubería como primer argumento, y perdería cualquier beneficio de la paralización. Pero si está de acuerdo con eso, puede hacer algo como esto:
def dask_pipe(initial_var, functions_args):
'''
call the dask_pipe with an init_var, and a list of functions
workflow, last_task = dask_pipe(initial_var, {function_1:[], function_2:[arg1, arg2]})
workflow, last_task = dask_pipe(initial_var, [function_1, function_2])
dask.get(workflow, last_task)
'''
workflow = {}
if isinstance(functions_args, list):
for ix, function in enumerate(functions_args):
if ix == 0:
workflow['task_' + str(ix)] = (function, initial_var)
else:
workflow['task_' + str(ix)] = (function, 'task_' + str(ix - 1))
return workflow, 'task_' + str(ix)
elif isinstance(functions_args, dict):
for ix, (function, args) in enumerate(functions_args.items()):
if ix == 0:
workflow['task_' + str(ix)] = (function, initial_var)
else:
workflow['task_' + str(ix)] = (function, 'task_' + str(ix - 1), *args )
return workflow, 'task_' + str(ix)
def foo(df):
return df[['a','b']]
def bar(df, s1, s2):
return df.columns.tolist() + [s1, s2]
def baz(df):
return df.columns.tolist()
import dask
import pandas as pd
df = pd.DataFrame({'a':[1,2,3],'b':[1,2,3],'c':[1,2,3]})
Ahora, con este contenedor, puede hacer una tubería siguiendo cualquiera de estos patrones sintácticos:
Me gusta esto:
workflow, last_task = dask_pipe(df, [foo, baz])
print(dask.get(workflow, last_task))
workflow, last_task = dask_pipe(df, {foo:[], bar:['string1', 'string2']})
print(dask.get(workflow, last_task))
crime_by_state %>% filter(State=="New York", Year==2005) ...
desde el final de How dplyr reemplazó mis modismos R. más comunes .