How to avoid two loops here in python - python
Here is my problem: I have a dict in python such as:
a = {1:[2, 3], 2:[1]}
I would like to output:
1, 2
1, 3
2, 1
what I am doing is
for i in a:
for j in a[i]:
print i, j
so is there any easier way to do that avoiding two loops here or it is the easiest way already?
The code you have is about as good as it gets. One minor improvement might be iterating over the dictionary's items in the outer loop, rather than doing indexing:
for i, lst in a.items() # use a.iteritems() in Python 2
for j in lst:
print("{}, {}".format(i, j))
Couple of alternatives using list comprehensions, if you want to avoid explicit for loops.
# 1 method
# Python2.7
for key, value in a.iteritems(): # Use a.items() for python 3
print "\n".join(["%d, %d" % (key, val) for val in value])
# 2 method - A more fancy way with list comprehensions
print "\n".join(["\n".join(["%d, %d" % (key, val) for val in value]) for key, value in a.iteritems()])
Both will output
1, 2
1, 3
2, 1
Remember in Python, Readability counts., so ideally #Blckknght's solution is what you should look forward, but just looking at your problem, technically as a POC, that you can rewrite your expression as a single loop, here is a solution.
But caveat, if you wan;t your code to be Readable, remember Explicit is better than implicit.
>>> def foo():
return '\n'.join('{},{}'.format(*e) for e in chain(*(izip(cycle([k]),v) for k,v in a.items())))
>>> def bar():
return '\n'.join("{},{}".format(i,j) for i in a for j in a[i])
>>> cProfile.run("foo()")
20 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <pyshell#240>:1(foo)
5 0.000 0.000 0.000 0.000 <pyshell#240>:2(<genexpr>)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
10 0.000 0.000 0.000 0.000 {method 'format' of 'str' objects}
1 0.000 0.000 0.000 0.000 {method 'items' of 'dict' objects}
1 0.000 0.000 0.000 0.000 {method 'join' of 'str' objects}
>>> cProfile.run("bar()")
25 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <pyshell#242>:1(bar)
11 0.000 0.000 0.000 0.000 <pyshell#242>:2(<genexpr>)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
10 0.000 0.000 0.000 0.000 {method 'format' of 'str' objects}
1 0.000 0.000 0.000 0.000 {method 'join' of 'str' objects}
Related
strange result from timeit
I tried to repeat the functionality of IPython %time, but for some strange reason, results of testing of some function are horrific. IPython: In [11]: from random import shuffle ....: import numpy as np ....: def numpy_seq_el_rank(seq, el): ....: return sum(seq < el) ....: ....: seq = np.array(xrange(10000)) ....: shuffle(seq) ....: In [12]: %timeit numpy_seq_el_rank(seq, 10000//2) 10000 loops, best of 3: 46.1 µs per loop Python: from timeit import timeit, repeat def my_timeit(code, setup, rep, loops): result = repeat(code, setup=setup, repeat=rep, number=loops) return '%d loops, best of %d: %0.9f sec per loop'%(loops, rep, min(result)) np_setup = ''' from random import shuffle import numpy as np def numpy_seq_el_rank(seq, el): return sum(seq < el) seq = np.array(xrange(10000)) shuffle(seq) ''' np_code = 'numpy_seq_el_rank(seq, 10000//2)' print 'Numpy seq_el_rank:\n\t%s'%my_timeit(code=np_code, setup=np_setup, rep=3, loops=100) And its output: Numpy seq_el_rank: 100 loops, best of 3: 1.655324947 sec per loop As you can see, in python i made 100 loops instead 10000 (and get 35000 times slower result) as in ipython, because it takes really long time. Can anybody explain why result in python is so slow? UPD: Here is cProfile.run('my_timeit(code=np_code, setup=np_setup, rep=3, loops=10000)') output: 30650 function calls in 4.987 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 4.987 4.987 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 <timeit-src>:2(<module>) 3 0.001 0.000 4.985 1.662 <timeit-src>:2(inner) 300 0.006 0.000 4.961 0.017 <timeit-src>:7(numpy_seq_el_rank) 1 0.000 0.000 4.987 4.987 Lab10.py:47(my_timeit) 3 0.019 0.006 0.021 0.007 random.py:277(shuffle) 1 0.000 0.000 0.002 0.002 timeit.py:121(__init__) 3 0.000 0.000 4.985 1.662 timeit.py:185(timeit) 1 0.000 0.000 4.985 4.985 timeit.py:208(repeat) 1 0.000 0.000 4.987 4.987 timeit.py:239(repeat) 2 0.000 0.000 0.000 0.000 timeit.py:90(reindent) 3 0.002 0.001 0.002 0.001 {compile} 3 0.000 0.000 0.000 0.000 {gc.disable} 3 0.000 0.000 0.000 0.000 {gc.enable} 3 0.000 0.000 0.000 0.000 {gc.isenabled} 1 0.000 0.000 0.000 0.000 {globals} 3 0.000 0.000 0.000 0.000 {isinstance} 3 0.000 0.000 0.000 0.000 {len} 3 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 29997 0.001 0.000 0.001 0.000 {method 'random' of '_random.Random' objects} 2 0.000 0.000 0.000 0.000 {method 'replace' of 'str' objects} 1 0.000 0.000 0.000 0.000 {min} 3 0.003 0.001 0.003 0.001 {numpy.core.multiarray.array} 1 0.000 0.000 0.000 0.000 {range} 300 4.955 0.017 4.955 0.017 {sum} 6 0.000 0.000 0.000 0.000 {time.clock}
Well, one issue is that you're misreading the results. ipython is telling you how long it took each of the 10,000 iterations for the set of 10,000 iterations with the lowest total time. The timeit.repeat module is reporting how long the whole round of 100 iterations took (again, for the shortest of three). So the real discrepancy is 46.1 µs per loop (ipython) vs. 16.5 ms per loop (python), still a factor of ~350x difference, but not 35,000x. You didn't show profiling results for ipython. Is it possible that in your ipython session, you did either from numpy import sum or from numpy import *? If so, you'd have been timing the numpy.sum (which is optimized for numpy arrays and would run several orders of magnitude faster), while your python code (which isolated the globals in a way that ipython does not) ran the normal sum (that has to convert all the values to Python ints and sum them). If you check your profiling output, virtually all of your work is being done in sum; if that part of your code was sped up by several orders of magnitude, the total time would reduce similarly. That would explain the "real" discrepancy; in the test case linked above, it was a 40x difference, and that was for a smaller array (the smaller the array, the less numpy can "show off") with more complex values (vs. summing 0s and 1s here I believe). The remainder (if any) is probably an issue of how the code is being evaled slightly differently, or possibly weirdness with the random shuffle (for consistent tests, you'd want to seed random with a consistent seed to make the "randomness" repeatable) but I doubt that's a difference of more than a few percent.
There could be any number of reasons this code is running slower in one implementation of python than another. One may be optimized differently than another, one may pre-compile certain parts while the other is fully interpreted. The only way to figure out why is to profile your code. https://docs.python.org/2/library/profile.html import cProfile cProfile.run('repeat(code, setup=setup, repeat=rep, number=loops)') Will give a result similar to ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.000 0.000 <stdin>:1(testing) 1 0.000 0.000 0.000 0.000 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1 0.000 0.000 0.000 0.000 {method 'upper' of 'str' objects} Which shows you when function calls were made, how many times they were made and how long they took.
Python numpy: sum every 3 rows (converting monthly to quarterly)
I have a set of one-dimensional numpy arrays with monthly data. I need to aggregate them by quarter, creating a new array where the first item is the sum of the first 3 items of the old array, etc. I am using this function, with x =3 : def sumeveryxrows(myarray,x): return([sum(myarray[x*n:x*n+x]) for n in range( int(len(myarray)/x))]) It works, but can you think of a faster way? I profiled it, and 97% of the time is spent doing __getitem__
You could use reshape (assuming your array has a size multiple of x): sumeveryxrows = lambda myarray, x: myarray.reshape((myarray.shape[0] / x, x)).sum(1) The above takes less than .3s on an array with 30000000 values: >>> a = numpy.random.rand(30000000) >>> cProfile.run('sumeveryxrows(a, 3)') 8 function calls in 0.263 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.258 0.258 <stdin>:1(<lambda>) 1 0.005 0.005 0.263 0.263 <string>:1(<module>) 1 0.000 0.000 0.258 0.258 _methods.py:31(_sum) 1 0.000 0.000 0.263 0.263 {built-in method exec} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1 0.258 0.258 0.258 0.258 {method 'reduce' of 'numpy.ufunc' objects} 1 0.000 0.000 0.000 0.000 {method 'reshape' of 'numpy.ndarray' objects} 1 0.000 0.000 0.258 0.258 {method 'sum' of 'numpy.ndarray' objects}
another solution may be def sumeveryxrows(myarray, x): return [sum(myarray[n: n+x]) for n in xrange(0, len(myarray), x)] This is for python 2.x. If your using python 3 replace xrange with range. xrange uses an iterator rather than generating an entire list. You can also specify a step. This removes the need to use multiplication. Then of course there is always the non-python way to do it (specifically for 3). def sumevery3rows(a): i = 0 ret = [] stop = len(a) - 2 while i < stop: ret.append(a[i] + a[i+1] + a[i+2]) i += 3 if i != len(a): ret.append(sum(a[i:len(a)])) return ret I don't know how well this performs, and an implementation for variable x would probably make any benefits of this solution non-existent.
Python getting meaningful results from cProfile
I have a Python script in a file which takes just over 30 seconds to run. I am trying to profile it as I would like to cut down this time dramatically. I am trying to profile the script using cProfile, but essentially all it seems to be telling me is that yes, the main script took a long time to run, but doesn't give the kind of breakdown I was expecting. At the terminal, I type something like: cat my_script_input.txt | python -m cProfile -s time my_script.py The results I get are: <my_script_output> 683121 function calls (682169 primitive calls) in 32.133 seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 1 31.980 31.980 32.133 32.133 my_script.py:18(<module>) 121089 0.050 0.000 0.050 0.000 {method 'split' of 'str' objects} 121090 0.038 0.000 0.049 0.000 fileinput.py:243(next) 2 0.027 0.014 0.036 0.018 {method 'sort' of 'list' objects} 121089 0.009 0.000 0.009 0.000 {method 'strip' of 'str' objects} 201534 0.009 0.000 0.009 0.000 {method 'append' of 'list' objects} 100858 0.009 0.000 0.009 0.000 my_script.py:51(<lambda>) 952 0.008 0.000 0.008 0.000 {method 'readlines' of 'file' objects} 1904/952 0.003 0.000 0.011 0.000 fileinput.py:292(readline) 14412 0.001 0.000 0.001 0.000 {method 'add' of 'set' objects} 182 0.000 0.000 0.000 0.000 {method 'join' of 'str' objects} 1 0.000 0.000 0.000 0.000 fileinput.py:80(<module>) 1 0.000 0.000 0.000 0.000 fileinput.py:197(__init__) 1 0.000 0.000 0.000 0.000 fileinput.py:266(nextfile) 1 0.000 0.000 0.000 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 fileinput.py:91(input) 1 0.000 0.000 0.000 0.000 fileinput.py:184(FileInput) 1 0.000 0.000 0.000 0.000 fileinput.py:240(__iter__) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} This doesn't seem to be telling me anything useful. The vast majority of the time is simply listed as: ncalls tottime percall cumtime percall filename:lineno(function) 1 31.980 31.980 32.133 32.133 my_script.py:18(<module>) In my_script.py, Line 18 is nothing more than the closing """ of the file's header block comment, so it's not that there is a whole load of work concentrated in Line 18. The script as a whole is mostly made up of line-based processing with mostly some string splitting, sorting and set work, so I was expecting to find the majority of time going to one or more of these activities. As it stands, seeing all the time grouped in cProfile's results as occurring on a comment line doesn't make any sense or at least does not shed any light on what is actually consuming all the time. EDIT: I've constructed a minimum working example similar to my above case to demonstrate the same behavior: mwe.py import fileinput for line in fileinput.input(): for i in range(10): y = int(line.strip()) + int(line.strip()) And call it with: perl -e 'for(1..1000000){print "$_\n"}' | python -m cProfile -s time mwe.py To get the result: 22002536 function calls (22001694 primitive calls) in 9.433 seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 1 8.004 8.004 9.433 9.433 mwe.py:1(<module>) 20000000 1.021 0.000 1.021 0.000 {method 'strip' of 'str' objects} 1000001 0.270 0.000 0.301 0.000 fileinput.py:243(next) 1000000 0.107 0.000 0.107 0.000 {range} 842 0.024 0.000 0.024 0.000 {method 'readlines' of 'file' objects} 1684/842 0.007 0.000 0.032 0.000 fileinput.py:292(readline) 1 0.000 0.000 0.000 0.000 fileinput.py:80(<module>) 1 0.000 0.000 0.000 0.000 fileinput.py:91(input) 1 0.000 0.000 0.000 0.000 fileinput.py:197(__init__) 1 0.000 0.000 0.000 0.000 fileinput.py:184(FileInput) 1 0.000 0.000 0.000 0.000 fileinput.py:266(nextfile) 1 0.000 0.000 0.000 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 fileinput.py:240(__iter__) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} Am I using cProfile incorrectly somehow?
As I mentioned in a comment, when you can't get cProfile to work externally, you can often use it internally instead. It's not that hard. For example, when I run with -m cProfile in my Python 2.7, I get effectively the same results you did. But when I manually instrument your example program: import fileinput import cProfile pr = cProfile.Profile() pr.enable() for line in fileinput.input(): for i in range(10): y = int(line.strip()) + int(line.strip()) pr.disable() pr.print_stats(sort='time') … here's what I get: 22002533 function calls (22001691 primitive calls) in 3.352 seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 20000000 2.326 0.000 2.326 0.000 {method 'strip' of 'str' objects} 1000001 0.646 0.000 0.700 0.000 fileinput.py:243(next) 1000000 0.325 0.000 0.325 0.000 {range} 842 0.042 0.000 0.042 0.000 {method 'readlines' of 'file' objects} 1684/842 0.013 0.000 0.055 0.000 fileinput.py:292(readline) 1 0.000 0.000 0.000 0.000 fileinput.py:197(__init__) 1 0.000 0.000 0.000 0.000 fileinput.py:91(input) 1 0.000 0.000 0.000 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 fileinput.py:266(nextfile) 1 0.000 0.000 0.000 0.000 fileinput.py:240(__iter__) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} That's a lot more useful: It tells you what you probably already expected, that more than half your time is spent calling str.strip(). Also, note that if you can't edit the file containing code you wish to profile (mwe.py), you can always do this: import cProfile pr = cProfile.Profile() pr.enable() import mwe pr.disable() pr.print_stats(sort='time') Even that doesn't always work. If your program calls exit(), for example, you'll have to use a try:/finally: wrapper and/or an atexit. And it it calls os._exit(), or segfaults, you're probably completely hosed. But that isn't very common. However, something I discovered later: If you move all code out of the global scope, -m cProfile seems to work, at least for this case. For example: import fileinput def f(): for line in fileinput.input(): for i in range(10): y = int(line.strip()) + int(line.strip()) f() Now the output from -m cProfile includes, among other things: 2000000 4.819 0.000 4.819 0.000 :0(strip) 100001 0.288 0.000 0.295 0.000 fileinput.py:243(next) I have no idea why this also made it twice as slow… or maybe that's just a cache effect; it's been a few minutes since I last ran it, and I've done lots of web browsing in between. But that's not important, what's important is that most of the time is getting charged to reasonable places. But if I change this to move the outer loop to the global level, and only its body into a function, most of the time disappears again. Another alternative, which I wouldn't suggest except as a last resort… I notice that if I use profile instead of cProfile, it works both internally and externally, charging time to the right calls. However, those calls are also about 5x slower. And there seems to be an additional 10 seconds of constant overhead (which gets charged to import profile if used internally, whatever's on line 1 if used externally). So, to find out that split is using 70% of my time, instead of waiting 4 seconds and doing 2.326 / 3.352, I have to wait 27 seconds, and do 10.93 / (26.34 - 10.01). Not much fun… One last thing: I get the same results with a CPython 3.4 dev build—correct results when used internally, everything charged to the first line of code when used externally. But PyPy 2.2/2.7.3 and PyPy3 2.1b1/3.2.3 both seem to give me correct results with -m cProfile. This may just mean that PyPy's cProfile is faked on top of profile because the pure-Python code is fast enough. Anyway, if someone can figure out/explain why -m cProfile isn't working, that would be great… but otherwise, this is usually a perfectly good workaround.
Single pass algorithm for finding the topX percent of items
I'm looking for a single-pass algorithm for finding the topX percent of floats in a stream where I do not know the total number ahead of time ... but its on the order of 5-30 million floats. It needs to be single-pass since the data is generated on the fly and recreate the exact stream a second time. The algorithm I have so far is to keep a sorted list of the topX items that I've seen so far. As the stream continues I enlarge the list as needed. Then I use bisect_left to find the insertion point if needed. Below is the algorithm I have so far: from bisect import bisect_left from random import uniform from itertools import islice def data_gen(num): for _ in xrange(num): yield uniform(0,1) def get_top_X_percent(iterable, percent = 0.01, min_guess = 1000): top_nums = sorted(list(islice(iterable, int(percent*min_guess)))) #get an initial guess for ind, val in enumerate(iterable, len(top_nums)): if int(percent*ind) > len(top_nums): top_nums.insert(0,None) newind = bisect_left(top_nums, val) if newind > 0: top_nums.insert(newind, val) top_nums.pop(0) return top_nums if __name__ == '__main__': num = 1000000 all_data = sorted(data_gen(num)) result = get_top_X_percent(all_data) assert result[0] == all_data[-int(num*0.01)], 'Too far off, lowest num:%f' % result[0] print result[0] In the real case the data does not come from any standard distribution (otherwise I could use some statistics knowledge). Any suggestions would be appreciated.
I'm not sure there's any way to actually do that reliably, as the range denoted by the "top X percent" can grow unpredictably as you see more elements. Consider the following input: 101 102 103 104 105 106 107 108 109 110 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... If you wanted the top 25% of elements, you'd end up picking 101 and 102 out of the first ten elements, but after seeing enough zeroes after there you'd eventually have to end up selecting all of the first ten. This same pattern can be expanded to any sufficiently large stream -- it's always possible to end up getting misled by appearances and discarding elements that you actually should have kept. As such, unless you know the exact length of the stream ahead of time, I don't think this is possible (short of keeping every element in memory until you hit the end of the stream).
You must store the entire stream in memory. Proof: You have a sequence of numbers, n1,…,nk. The value of k is unknown. How do you know when ni can be forgotten? When you have seen x*k/100 numbers greater than ni. However, since k is unknown, you can never do this. So the only "one-pass" algorithm must store the entire sequence in memory.
As the other answers have discussed, you can't really do a whole lot better than just storing the entire stream in memory. Consider doing it that way, especially since 5-30 million floats is probably only 40-240 MB of memory which is manageable. Given that you store the entire stream, the algorithmically fastest way to get the topX percent is by first finding the cutoff element (the smallest element that is in the topX percent) using a linear-time selection algorithm: http://en.wikipedia.org/wiki/Selection_algorithm Then, make another pass through the stream and filter out all the elements smaller than the cutoff element. This method is linear time and linear space, which is the best you can hope for.
Below is the output from running this with cProfile. It seems that your code is doing fine, as most calls are 0.000 (percall). It seems its slow simply because you have many items to process. If you wanted to optimize, you'd have to try and pop less items, as that is what has it has been called 999999, which seems to be unnecessary. Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.000 0.000 __future__.py:48(<module>) 1 0.000 0.000 0.000 0.000 __future__.py:74(_Feature) 7 0.000 0.000 0.000 0.000 __future__.py:75(__init__) 1 0.001 0.001 0.001 0.001 bisect.py:1(<module>) 1 0.001 0.001 0.001 0.001 hashlib.py:55(<module>) 6 0.000 0.000 0.000 0.000 hashlib.py:91(__get_openssl_constructor) 1 0.000 0.000 0.000 0.000 os.py:743(urandom) 1 0.000 0.000 0.000 0.000 random.py:100(seed) 1000000 0.731 0.000 0.876 0.000 random.py:355(uniform) 1 0.003 0.003 0.004 0.004 random.py:40(<module>) 1 0.000 0.000 0.000 0.000 random.py:647(WichmannHill) 1 0.000 0.000 0.000 0.000 random.py:72(Random) 1 0.000 0.000 0.000 0.000 random.py:797(SystemRandom) 1 0.000 0.000 0.000 0.000 random.py:91(__init__) 1 2.498 2.498 13.313 13.313 test.py:12(get_top_X_percent) 1 0.006 0.006 16.330 16.330 test.py:3(<module>) 1000001 0.545 0.000 1.422 0.000 test.py:8(data_gen) 1000000 1.744 0.000 1.744 0.000 {_bisect.bisect_left} 1 0.000 0.000 0.000 0.000 {_hashlib.openssl_md5} 1 0.000 0.000 0.000 0.000 {_hashlib.openssl_sha1} 1 0.000 0.000 0.000 0.000 {_hashlib.openssl_sha224} 1 0.000 0.000 0.000 0.000 {_hashlib.openssl_sha256} 1 0.000 0.000 0.000 0.000 {_hashlib.openssl_sha384} 1 0.000 0.000 0.000 0.000 {_hashlib.openssl_sha512} 1 0.000 0.000 0.000 0.000 {binascii.hexlify} 1 0.000 0.000 0.000 0.000 {function seed at 0x100684a28} 6 0.000 0.000 0.000 0.000 {getattr} 6 0.000 0.000 0.000 0.000 {globals} 1000004 0.125 0.000 0.125 0.000 {len} 1 0.000 0.000 0.000 0.000 {math.exp} 2 0.000 0.000 0.000 0.000 {math.log} 1 0.000 0.000 0.000 0.000 {math.sqrt} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1009989 0.469 0.000 0.469 0.000 {method 'insert' of 'list' objects} 999999 8.477 0.000 8.477 0.000 {method 'pop' of 'list' objects} 1000000 0.146 0.000 0.146 0.000 {method 'random' of '_random.Random' objects} 1 0.000 0.000 0.000 0.000 {posix.close} 1 0.000 0.000 0.000 0.000 {posix.open} 1 0.000 0.000 0.000 0.000 {posix.read} 2 1.585 0.792 3.006 1.503 {sorted} BTW, you can use cProfile with: python -m cProfile test.py
Flattening a shallow list in Python [duplicate]
This question already has answers here: How do I make a flat list out of a list of lists? (34 answers) Closed 6 years ago. Is there a simple way to flatten a list of iterables with a list comprehension, or failing that, what would you all consider to be the best way to flatten a shallow list like this, balancing performance and readability? I tried to flatten such a list with a nested list comprehension, like this: [image for image in menuitem for menuitem in list_of_menuitems] But I get in trouble of the NameError variety there, because the name 'menuitem' is not defined. After googling and looking around on Stack Overflow, I got the desired results with a reduce statement: reduce(list.__add__, map(lambda x: list(x), list_of_menuitems)) But this method is fairly unreadable because I need that list(x) call there because x is a Django QuerySet object. Conclusion: Thanks to everyone who contributed to this question. Here is a summary of what I learned. I'm also making this a community wiki in case others want to add to or correct these observations. My original reduce statement is redundant and is better written this way: >>> reduce(list.__add__, (list(mi) for mi in list_of_menuitems)) This is the correct syntax for a nested list comprehension (Brilliant summary dF!): >>> [image for mi in list_of_menuitems for image in mi] But neither of these methods are as efficient as using itertools.chain: >>> from itertools import chain >>> list(chain(*list_of_menuitems)) And as #cdleary notes, it's probably better style to avoid * operator magic by using chain.from_iterable like so: >>> chain = itertools.chain.from_iterable([[1,2],[3],[5,89],[],[6]]) >>> print(list(chain)) >>> [1, 2, 3, 5, 89, 6]
If you're just looking to iterate over a flattened version of the data structure and don't need an indexable sequence, consider itertools.chain and company. >>> list_of_menuitems = [['image00', 'image01'], ['image10'], []] >>> import itertools >>> chain = itertools.chain(*list_of_menuitems) >>> print(list(chain)) ['image00', 'image01', 'image10'] It will work on anything that's iterable, which should include Django's iterable QuerySets, which it appears that you're using in the question. Edit: This is probably as good as a reduce anyway, because reduce will have the same overhead copying the items into the list that's being extended. chain will only incur this (same) overhead if you run list(chain) at the end. Meta-Edit: Actually, it's less overhead than the question's proposed solution, because you throw away the temporary lists you create when you extend the original with the temporary. Edit: As J.F. Sebastian says itertools.chain.from_iterable avoids the unpacking and you should use that to avoid * magic, but the timeit app shows negligible performance difference.
You almost have it! The way to do nested list comprehensions is to put the for statements in the same order as they would go in regular nested for statements. Thus, this for inner_list in outer_list: for item in inner_list: ... corresponds to [... for inner_list in outer_list for item in inner_list] So you want [image for menuitem in list_of_menuitems for image in menuitem]
#S.Lott: You inspired me to write a timeit app. I figured it would also vary based on the number of partitions (number of iterators within the container list) -- your comment didn't mention how many partitions there were of the thirty items. This plot is flattening a thousand items in every run, with varying number of partitions. The items are evenly distributed among the partitions. Code (Python 2.6): #!/usr/bin/env python2.6 """Usage: %prog item_count""" from __future__ import print_function import collections import itertools import operator from timeit import Timer import sys import matplotlib.pyplot as pyplot def itertools_flatten(iter_lst): return list(itertools.chain(*iter_lst)) def itertools_iterable_flatten(iter_iter): return list(itertools.chain.from_iterable(iter_iter)) def reduce_flatten(iter_lst): return reduce(operator.add, map(list, iter_lst)) def reduce_lambda_flatten(iter_lst): return reduce(operator.add, map(lambda x: list(x), [i for i in iter_lst])) def comprehension_flatten(iter_lst): return list(item for iter_ in iter_lst for item in iter_) METHODS = ['itertools', 'itertools_iterable', 'reduce', 'reduce_lambda', 'comprehension'] def _time_test_assert(iter_lst): """Make sure all methods produce an equivalent value. :raise AssertionError: On any non-equivalent value.""" callables = (globals()[method + '_flatten'] for method in METHODS) results = [callable(iter_lst) for callable in callables] if not all(result == results[0] for result in results[1:]): raise AssertionError def time_test(partition_count, item_count_per_partition, test_count=10000): """Run flatten methods on a list of :param:`partition_count` iterables. Normalize results over :param:`test_count` runs. :return: Mapping from method to (normalized) microseconds per pass. """ iter_lst = [[dict()] * item_count_per_partition] * partition_count print('Partition count: ', partition_count) print('Items per partition:', item_count_per_partition) _time_test_assert(iter_lst) test_str = 'flatten(%r)' % iter_lst result_by_method = {} for method in METHODS: setup_str = 'from test import %s_flatten as flatten' % method t = Timer(test_str, setup_str) per_pass = test_count * t.timeit(number=test_count) / test_count print('%20s: %.2f usec/pass' % (method, per_pass)) result_by_method[method] = per_pass return result_by_method if __name__ == '__main__': if len(sys.argv) != 2: raise ValueError('Need a number of items to flatten') item_count = int(sys.argv[1]) partition_counts = [] pass_times_by_method = collections.defaultdict(list) for partition_count in xrange(1, item_count): if item_count % partition_count != 0: continue items_per_partition = item_count / partition_count result_by_method = time_test(partition_count, items_per_partition) partition_counts.append(partition_count) for method, result in result_by_method.iteritems(): pass_times_by_method[method].append(result) for method, pass_times in pass_times_by_method.iteritems(): pyplot.plot(partition_counts, pass_times, label=method) pyplot.legend() pyplot.title('Flattening Comparison for %d Items' % item_count) pyplot.xlabel('Number of Partitions') pyplot.ylabel('Microseconds') pyplot.show() Edit: Decided to make it community wiki. Note: METHODS should probably be accumulated with a decorator, but I figure it'd be easier for people to read this way.
sum(list_of_lists, []) would flatten it. l = [['image00', 'image01'], ['image10'], []] print sum(l,[]) # prints ['image00', 'image01', 'image10']
This solution works for arbitrary nesting depths - not just the "list of lists" depth that some (all?) of the other solutions are limited to: def flatten(x): result = [] for el in x: if hasattr(el, "__iter__") and not isinstance(el, basestring): result.extend(flatten(el)) else: result.append(el) return result It's the recursion which allows for arbitrary depth nesting - until you hit the maximum recursion depth, of course...
In Python 2.6, using chain.from_iterable(): >>> from itertools import chain >>> list(chain.from_iterable(mi.image_set.all() for mi in h.get_image_menu())) It avoids creating of intermediate list.
Performance Results. Revised. import itertools def itertools_flatten( aList ): return list( itertools.chain(*aList) ) from operator import add def reduce_flatten1( aList ): return reduce(add, map(lambda x: list(x), [mi for mi in aList])) def reduce_flatten2( aList ): return reduce(list.__add__, map(list, aList)) def comprehension_flatten( aList ): return list(y for x in aList for y in x) I flattened a 2-level list of 30 items 1000 times itertools_flatten 0.00554 comprehension_flatten 0.00815 reduce_flatten2 0.01103 reduce_flatten1 0.01404 Reduce is always a poor choice.
There seems to be a confusion with operator.add! When you add two lists together, the correct term for that is concat, not add. operator.concat is what you need to use. If you're thinking functional, it is as easy as this:: >>> from functools import reduce >>> import operator >>> list2d = ((1,2,3),(4,5,6), (7,), (8,9)) >>> reduce(operator.concat, list2d) (1, 2, 3, 4, 5, 6, 7, 8, 9) You see reduce respects the sequence type, so when you supply a tuple, you get back a tuple. let's try with a list:: >>> list2d = [[1,2,3],[4,5,6], [7], [8,9]] >>> reduce(operator.concat, list2d) [1, 2, 3, 4, 5, 6, 7, 8, 9] Aha, you get back a list. How about performance:: >>> list2d = [[1,2,3],[4,5,6], [7], [8,9]] >>> %timeit list(itertools.chain.from_iterable(list2d)) 1000000 loops, best of 3: 1.36 µs per loop from_iterable is pretty fast! But it's no comparison to reduce with concat. >>> list2d = ((1,2,3),(4,5,6), (7,), (8,9)) >>> %timeit reduce(operator.concat, list2d) 1000000 loops, best of 3: 492 ns per loop
Here is the correct solution using list comprehensions (they're backward in the question): >>> join = lambda it: (y for x in it for y in x) >>> list(join([[1,2],[3,4,5],[]])) [1, 2, 3, 4, 5] In your case it would be [image for menuitem in list_of_menuitems for image in menuitem.image_set.all()] or you could use join and say join(menuitem.image_set.all() for menuitem in list_of_menuitems) In either case, the gotcha was the nesting of the for loops.
Off the top of my head, you can eliminate the lambda: reduce(list.__add__, map(list, [mi.image_set.all() for mi in list_of_menuitems])) Or even eliminate the map, since you've already got a list-comp: reduce(list.__add__, [list(mi.image_set.all()) for mi in list_of_menuitems]) You can also just express this as a sum of lists: sum([list(mi.image_set.all()) for mi in list_of_menuitems], [])
This version is a generator.Tweak it if you want a list. def list_or_tuple(l): return isinstance(l,(list,tuple)) ## predicate will select the container to be flattened ## write your own as required ## this one flattens every list/tuple def flatten(seq,predicate=list_or_tuple): ## recursive generator for i in seq: if predicate(seq): for j in flatten(i): yield j else: yield i You can add a predicate ,if want to flatten those which satisfy a condition Taken from python cookbook
If you have to flat a more complicated list with not iterable elements or with depth more than 2 you can use following function: def flat_list(list_to_flat): if not isinstance(list_to_flat, list): yield list_to_flat else: for item in list_to_flat: yield from flat_list(item) It will return a generator object which you can convert to a list with list() function. Notice that yield from syntax is available starting from python3.3, but you can use explicit iteration instead. Example: >>> a = [1, [2, 3], [1, [2, 3, [1, [2, 3]]]]] >>> print(list(flat_list(a))) [1, 2, 3, 1, 2, 3, 1, 2, 3]
Here is a version working for multiple levels of list using collectons.Iterable: import collections def flatten(o, flatten_condition=lambda i: isinstance(i, collections.Iterable) and not isinstance(i, str)): result = [] for i in o: if flatten_condition(i): result.extend(flatten(i, flatten_condition)) else: result.append(i) return result
have you tried flatten? From matplotlib.cbook.flatten(seq, scalarp=) ? l=[[1,2,3],[4,5,6], [7], [8,9]]*33 run("list(flatten(l))") 3732 function calls (3303 primitive calls) in 0.007 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.007 0.007 <string>:1(<module>) 429 0.001 0.000 0.001 0.000 cbook.py:475(iterable) 429 0.002 0.000 0.003 0.000 cbook.py:484(is_string_like) 429 0.002 0.000 0.006 0.000 cbook.py:565(is_scalar_or_string) 727/298 0.001 0.000 0.007 0.000 cbook.py:605(flatten) 429 0.000 0.000 0.001 0.000 core.py:5641(isMaskedArray) 858 0.001 0.000 0.001 0.000 {isinstance} 429 0.000 0.000 0.000 0.000 {iter} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} l=[[1,2,3],[4,5,6], [7], [8,9]]*66 run("list(flatten(l))") 7461 function calls (6603 primitive calls) in 0.007 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.007 0.007 <string>:1(<module>) 858 0.001 0.000 0.001 0.000 cbook.py:475(iterable) 858 0.002 0.000 0.003 0.000 cbook.py:484(is_string_like) 858 0.002 0.000 0.006 0.000 cbook.py:565(is_scalar_or_string) 1453/595 0.001 0.000 0.007 0.000 cbook.py:605(flatten) 858 0.000 0.000 0.001 0.000 core.py:5641(isMaskedArray) 1716 0.001 0.000 0.001 0.000 {isinstance} 858 0.000 0.000 0.000 0.000 {iter} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} l=[[1,2,3],[4,5,6], [7], [8,9]]*99 run("list(flatten(l))") 11190 function calls (9903 primitive calls) in 0.010 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.010 0.010 <string>:1(<module>) 1287 0.002 0.000 0.002 0.000 cbook.py:475(iterable) 1287 0.003 0.000 0.004 0.000 cbook.py:484(is_string_like) 1287 0.002 0.000 0.009 0.000 cbook.py:565(is_scalar_or_string) 2179/892 0.001 0.000 0.010 0.000 cbook.py:605(flatten) 1287 0.001 0.000 0.001 0.000 core.py:5641(isMaskedArray) 2574 0.001 0.000 0.001 0.000 {isinstance} 1287 0.000 0.000 0.000 0.000 {iter} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} l=[[1,2,3],[4,5,6], [7], [8,9]]*132 run("list(flatten(l))") 14919 function calls (13203 primitive calls) in 0.013 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.013 0.013 <string>:1(<module>) 1716 0.002 0.000 0.002 0.000 cbook.py:475(iterable) 1716 0.004 0.000 0.006 0.000 cbook.py:484(is_string_like) 1716 0.003 0.000 0.011 0.000 cbook.py:565(is_scalar_or_string) 2905/1189 0.002 0.000 0.013 0.000 cbook.py:605(flatten) 1716 0.001 0.000 0.001 0.000 core.py:5641(isMaskedArray) 3432 0.001 0.000 0.001 0.000 {isinstance} 1716 0.001 0.000 0.001 0.000 {iter} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' UPDATE Which gave me another idea: l=[[1,2,3],[4,5,6], [7], [8,9]]*33 run("flattenlist(l)") 564 function calls (432 primitive calls) in 0.000 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 133/1 0.000 0.000 0.000 0.000 <ipython-input-55-39b139bad497>:4(flattenlist) 1 0.000 0.000 0.000 0.000 <string>:1(<module>) 429 0.000 0.000 0.000 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} l=[[1,2,3],[4,5,6], [7], [8,9]]*66 run("flattenlist(l)") 1125 function calls (861 primitive calls) in 0.001 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 265/1 0.001 0.000 0.001 0.001 <ipython-input-55-39b139bad497>:4(flattenlist) 1 0.000 0.000 0.001 0.001 <string>:1(<module>) 858 0.000 0.000 0.000 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} l=[[1,2,3],[4,5,6], [7], [8,9]]*99 run("flattenlist(l)") 1686 function calls (1290 primitive calls) in 0.001 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 397/1 0.001 0.000 0.001 0.001 <ipython-input-55-39b139bad497>:4(flattenlist) 1 0.000 0.000 0.001 0.001 <string>:1(<module>) 1287 0.000 0.000 0.000 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} l=[[1,2,3],[4,5,6], [7], [8,9]]*132 run("flattenlist(l)") 2247 function calls (1719 primitive calls) in 0.002 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 529/1 0.001 0.000 0.002 0.002 <ipython-input-55-39b139bad497>:4(flattenlist) 1 0.000 0.000 0.002 0.002 <string>:1(<module>) 1716 0.001 0.000 0.001 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} l=[[1,2,3],[4,5,6], [7], [8,9]]*1320 run("flattenlist(l)") 22443 function calls (17163 primitive calls) in 0.016 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 5281/1 0.011 0.000 0.016 0.016 <ipython-input-55-39b139bad497>:4(flattenlist) 1 0.000 0.000 0.016 0.016 <string>:1(<module>) 17160 0.005 0.000 0.005 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} So to test how effective it is when recursive gets deeper: How much deeper? l=[[1,2,3],[4,5,6], [7], [8,9]]*1320 new=[l]*33 run("flattenlist(new)") 740589 function calls (566316 primitive calls) in 0.418 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 174274/1 0.281 0.000 0.417 0.417 <ipython-input-55-39b139bad497>:4(flattenlist) 1 0.001 0.001 0.418 0.418 <string>:1(<module>) 566313 0.136 0.000 0.136 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} new=[l]*66 run("flattenlist(new)") 1481175 function calls (1132629 primitive calls) in 0.809 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 348547/1 0.542 0.000 0.807 0.807 <ipython-input-55-39b139bad497>:4(flattenlist) 1 0.002 0.002 0.809 0.809 <string>:1(<module>) 1132626 0.266 0.000 0.266 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} new=[l]*99 run("flattenlist(new)") 2221761 function calls (1698942 primitive calls) in 1.211 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 522820/1 0.815 0.000 1.208 1.208 <ipython-input-55-39b139bad497>:4(flattenlist) 1 0.002 0.002 1.211 1.211 <string>:1(<module>) 1698939 0.393 0.000 0.393 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} new=[l]*132 run("flattenlist(new)") 2962347 function calls (2265255 primitive calls) in 1.630 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 697093/1 1.091 0.000 1.627 1.627 <ipython-input-55-39b139bad497>:4(flattenlist) 1 0.003 0.003 1.630 1.630 <string>:1(<module>) 2265252 0.536 0.000 0.536 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} new=[l]*1320 run("flattenlist(new)") 29623443 function calls (22652523 primitive calls) in 16.103 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 6970921/1 10.842 0.000 16.069 16.069 <ipython-input-55-39b139bad497>:4(flattenlist) 1 0.034 0.034 16.103 16.103 <string>:1(<module>) 22652520 5.227 0.000 5.227 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} I will bet "flattenlist" I am going to use this rather than matploblib for a long long time unless I want a yield generator and fast result as "flatten" uses in matploblib.cbook This, is fast. And here is the code : typ=(list,tuple) def flattenlist(d): thelist = [] for x in d: if not isinstance(x,typ): thelist += [x] else: thelist += flattenlist(x) return thelist
From my experience, the most efficient way to flatten a list of lists is: flat_list = [] map(flat_list.extend, list_of_list) Some timeit comparisons with the other proposed methods: list_of_list = [range(10)]*1000 %timeit flat_list=[]; map(flat_list.extend, list_of_list) #10000 loops, best of 3: 119 µs per loop %timeit flat_list=list(itertools.chain.from_iterable(list_of_list)) #1000 loops, best of 3: 210 µs per loop %timeit flat_list=[i for sublist in list_of_list for i in sublist] #1000 loops, best of 3: 525 µs per loop %timeit flat_list=reduce(list.__add__,list_of_list) #100 loops, best of 3: 18.1 ms per loop Now, the efficiency gain appears better when processing longer sublists: list_of_list = [range(1000)]*10 %timeit flat_list=[]; map(flat_list.extend, list_of_list) #10000 loops, best of 3: 60.7 µs per loop %timeit flat_list=list(itertools.chain.from_iterable(list_of_list)) #10000 loops, best of 3: 176 µs per loop And this methods also works with any iterative object: class SquaredRange(object): def __init__(self, n): self.range = range(n) def __iter__(self): for i in self.range: yield i**2 list_of_list = [SquaredRange(5)]*3 flat_list = [] map(flat_list.extend, list_of_list) print flat_list #[0, 1, 4, 9, 16, 0, 1, 4, 9, 16, 0, 1, 4, 9, 16]
def is_iterable(item): return isinstance(item, list) or isinstance(item, tuple) def flatten(items): for i in items: if is_iterable(item): for m in flatten(i): yield m else: yield i Test: print list(flatten2([1.0, 2, 'a', (4,), ((6,), (8,)), (((8,),(9,)), ((12,),(10)))]))
What about: from operator import add reduce(add, map(lambda x: list(x.image_set.all()), [mi for mi in list_of_menuitems])) But, Guido is recommending against performing too much in a single line of code since it reduces readability. There is minimal, if any, performance gain by performing what you want in a single line vs. multiple lines.
pylab provides a flatten: link to numpy flatten
If you're looking for a built-in, simple, one-liner you can use: a = [[1, 2, 3], [4, 5, 6] b = [i[x] for i in a for x in range(len(i))] print b returns [1, 2, 3, 4, 5, 6]
If each item in the list is a string (and any strings inside those strings use " " rather than ' '), you can use regular expressions (re module) >>> flattener = re.compile("\'.*?\'") >>> flattener <_sre.SRE_Pattern object at 0x10d439ca8> >>> stred = str(in_list) >>> outed = flattener.findall(stred) The above code converts in_list into a string, uses the regex to find all the substrings within quotes (i.e. each item of the list) and spits them out as a list.
A simple alternative is to use numpy's concatenate but it converts the contents to float: import numpy as np print np.concatenate([[1,2],[3],[5,89],[],[6]]) # array([ 1., 2., 3., 5., 89., 6.]) print list(np.concatenate([[1,2],[3],[5,89],[],[6]])) # [ 1., 2., 3., 5., 89., 6.]
The easiest way to achieve this in either Python 2 or 3 is to use the morph library using pip install morph. The code is: import morph list = [[1,2],[3],[5,89],[],[6]] flattened_list = morph.flatten(list) # returns [1, 2, 3, 5, 89, 6]
In Python 3.4 you will be able to do: [*innerlist for innerlist in outer_list]