0

I have a list of functions. Each function may return either a float or a list of 2 floats. I need to build a flat list of results using a list comprhension. For efficiency reasons, I need to build the results using one single iteration. For the same reason, the for loop is not an option (this is my belief... Is this correct?).

Input:

funcs=[[function_1, arguments_function_1],[function_2,arguments_function_2]...[function_n, arguments_function_n]

where function_j is a previously defined function. arguments_function_j is the list of arguments for funtion_j.

Tentative call:

results=[f[0](f[1]) for f in funcs]  

The above call is syntactically correct but does not return the results in the format I need. In facts, assuming that the forth function returns a list, the final result would be

[1,2,3,[4,5],6] instead of  [1,2,3,4,5,6] as I need

I tried the following:

results=[f[0](f[1]) if type(f[0](f[1]))==float\ 
else f[0](f[1])[0],f[0](f[1])[1]\
for f in funcs]  

But the f[0](f[1])[0],f[0](f[1])[1] part is not syntactically correct. It should be placed between bracktes but then it would not solve the problem.

Any hint? Thanks in advance

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Mauro Gentile
  • 1,463
  • 6
  • 26
  • 37
  • Possible duplicate of [Making a flat list out of list of lists in Python](https://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python) – Jean-François Fabre Sep 13 '17 at 07:36
  • please check if the original question answers yours. – Jean-François Fabre Sep 13 '17 at 07:36
  • These questions should help: - https://stackoverflow.com/questions/1077015/python-list-comprehensions-compressing-a-list-of-lists - https://stackoverflow.com/questions/21418764/flatmap-or-bind-in-python-3 – kenfire Sep 13 '17 at 07:43
  • Hi, yes I read that post but it does not fix my problem. The suggested solution there, assumes that ALL elements are lists like l=[[1],[2],[3,4],[5]]. My case is different. In my case the input is l=[1,2,[3,4],5] and the suggested solution rises an error. – Mauro Gentile Sep 13 '17 at 07:49

2 Answers2

2

You can create it with a comprehension using a helper function, but don't forget to time it vs a loop to see if you actually gain performance.

import collections
def sequencify(x):
  return x if isinstance(x, collections.Sequence) else (x,)
results = [x for (f, a) in funcs for x in sequencify(f(a))]

You can also test whether changing sequencify to your specific use case makes it faster, e.g

sequencify = lambda x: (x,) if type(f) == float else x

But, in truth, if you're looking for performance consider modifying the functions to return a tuple with 1 float instead of just a float and then you don't need the expensive type checks and can use the standard list-flattening technique:

results = [x for (f, a) in funcs for x in f(a)]

EDIT - my quick tests show that returning tuples of floats is faster than lists of floats by almost 25%:

$ python3 -m timeit -s 'import random; funcs=[ (random.choice([lambda a: [a], lambda a: [a,a]]),random.random()) for i in range(10000) ]' 'result=[x for (f, a) in funcs for x in f(a)]'
100 loops, best of 3: 2.98 msec per loop

$ python3 -m timeit -s 'import random; funcs=[ (random.choice([lambda a: (a,), lambda a: (a,a)]),random.random()) for i in range(10000) ]' 'result=[x for (f, a) in funcs for x in f(a)]'
100 loops, best of 3: 2.27 msec per loop
orip
  • 73,323
  • 21
  • 116
  • 148
1

you just need to chain your values so instead of

results=[f[0](f[1]) for f in funcs]  

just do

import itertools
result = list(itertools.chain.from_iterable(f[0](f[1]) for f in funcs))

(that will flatten your list of results, wrapped in list to force iteration)

see also: Making a flat list out of list of lists in Python

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219