I'm using Python 2.7 and have the following:
my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
I'd like create a 1-d list where the elements are ordered by position in sublist and then order of sublist. So the correct output for the above list is:
[1, 4, 7, 2, 5, 8, 3, 6, 9]
Here's my (incorrect) attempt:
def reorder_and_flatten(my_list):
my_list = [item for sublist in my_list for item in sublist]
result_nums = []
for i in range(len(my_list)):
result_nums.extend(my_list[i::3])
return result_nums
result = reorder_and_flatten(my_list)
This flattens my 2-d list and gives me:
[1, 4, 7, 2, 5, 8, 3, 6, 9, 4, 7, 5, 8, 6, 9, 7, 8, 9]
The first half of this list is correct but the second isn't.
I'd also like my function to be able to handle only 2 sublists. For instance, if given:
[[1, 2, 3], [], [7, 8, 9]
the correct output is:
[1, 7, 2, 8, 3, 9]
Any thoughts?
Thanks!
You're attempting to flatten, and then reorder, which makes things a lot harder than reordering and then flattening.
First, for your initial problem, that's just "unzip", as explained in the docs for zip:
>>> my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> list(zip(*my_list))
... [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
(In Python 2.7, you could just write zip(…) here instead of list(zip(…)), but this way, the same demonstration works identically in both 2.x and 3.x.)
And then, you already know how to flatten that:
>>> [item for sublist in zip(*my_list) for item in sublist]
[1, 4, 7, 2, 5, 8, 3, 6, 9]
But things get a bit more complicated for your second case, where some of the lists may be empty (or maybe just shorter?).
There's no function that's like zip but skips over missing values. You can write one pretty easily. But instead… there is a function that's like zip but fills in missing values with None (or anything else you prefer), izip_longest. So, we can just use that, then filter out the None values as we flatten:
>>> my_list = [[1, 2, 3], [], [7, 8, 9]]
>>> from itertools import izip_longest
>>> list(izip_longest(*my_list))
[(1, None, 7), (2, None, 8), (3, None, 9)]
>>> [item for sublist in izip_longest(*my_list) for item in sublist if item is not None]
[1, 7, 2, 8, 3, 9]
(In Python 3, the function izip_longest is renamed zip_longest.)
It's worth noting that the roundrobin recipe, as covered by ShadowRanger's answer, is an even nicer solution to this problem, and even easier to use (just copy and paste it from the docs, or pip install more_itertools and use it from there). It is a bit harder to understand—but it's worth taking the time to understand it (and asking for help if you get stuck).
result = [l[i] for i in range(max(len(v) for v in my_list)) for l in my_list if l]
i.e.
my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[l[i] for i in range(max(len(v) for v in my_list)) for l in my_list if l]
# => [1, 4, 7, 2, 5, 8, 3, 6, 9]
my_list = [[1, 2, 3], [], [7, 8, 9]]
[l[i] for i in range(max(len(v) for v in my_list)) for l in my_list if l]
# => [1, 7, 2, 8, 3, 9]
The itertools module's recipes section provides a roundrobin recipe that would do exactly what you want. It produces a generator, but your expected behavior would be seen with:
# define roundrobin recipe here
from itertools import cycle, islice
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
pending = len(iterables)
nexts = cycle(iter(it).next for it in iterables)
while pending:
try:
for next in nexts:
yield next()
except StopIteration:
pending -= 1
nexts = cycle(islice(nexts, pending))
def reorder_and_flatten(my_list):
return list(roundrobin(*my_list))
Your original code's main issue is that it looped over for i in range(len(my_list)):, extending with my_list[i::3]. Problem is, this ends up duplicating elements from index 3 onwards (index 3 was already selected as the second element of the index 0 slice). There are lots of other small logic errors here, so it's much easier to reuse a recipe.
This will be fairly performant, and generalize better than most hand-rolled solutions (it will round robin correctly even if the sublists are of uneven length, and it doesn't require second pass filtering or special handling of any kind to allow None as a value like zip_longest does).
If you are happy to use a 3rd party library, you can use NumPy and np.ndarray.ravel:
import numpy as np
A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
res_a = A.ravel('F') # array([1, 4, 7, 2, 5, 8, 3, 6, 9])
For the case where you have one or more empty lists, you can use filter to remove empty lists:
B = np.array(list(filter(None, [[1, 2, 3], [], [7, 8, 9]])))
res_b = B.ravel('F') # array([1, 7, 2, 8, 3, 9])
Both solutions require non-empty sublists to contain the same number of items. If list conversion is necessary you can use, for example, res_a.tolist().
While these "black box" methods won't teach you much, they will be faster for large arrays than list-based operations. See also What are the advantages of NumPy over regular Python lists?
Related
I would like a list to be stored into another list from right to left diagonally without importing anything if possible
eg. list =
[[1, 4, 6]
[6, 3, 7]
[2, 7, 9]]
say I'd like to store [6, 3, 2] into another list, how would i go about doing it? I have tried many ways for hours and still cant find a solution
With a list comprehension:
l =[[1, 4, 6],
[6, 3, 7],
[2, 7, 9]]
diagonal = [row[-i] for i, row in enumerate(l, start=1)]
print(diagonal)
Output
[6, 3, 2]
The following snipped
l =[[1, 4, 6],
[6, 3, 7],
[2, 7, 9]]
d = len(l)
a = []
for i in range(0,d):
a.append(l[i][d-1-i])
print(a)
results in the output you expected:
[6, 3, 2]
You can use a list comprehension and use list indexing twice to select your row and column:
L = [[1, 4, 6],
[6, 3, 7],
[2, 7, 9]]
n = len(L)
res = [L[i][n-i-1] for i in range(n)]
# [6, 3, 2]
An alternative formulation is to use enumerate as per #OlivierMelançon's solution.
If you can use a 3rd party library, you can use NumPy to extract the diagonal of a flipped array:
import numpy as np
arr = np.array(L)
res = np.diag(np.fliplr(arr))
# array([6, 3, 2])
When you want to create a list out from another list, list comprehension is a very good way to go.
a = yourlist
print([a[i][(i+1)*-1] for i in range(len(a))])
This list comprehension loops through the lists taking the the furthes back integer and the second furthes back and so on.
Using numpy and rotate (90)
import numpy as np
list = [[1, 4, 6],[6, 3, 7],[2, 7, 9]]
np.diag(np.rot90(array))
Output :
array([6, 3, 2])
or without using numpy:
list = [[1, 4, 6],[6, 3, 7],[2, 7, 9]]
res=[]
i=-1
for elm in list :
res.append(elm[i])
i-=1
print res
#[6, 3, 2]
I have a number two, I have this list, how would I go about removing multiples of the number 2 from that list and update it?
10 [2, 3, 4, 5, 6, 7, 8, 9, 10] 2
Assuming you have a list l and a number n you can remove all multiples of n from l with a list comprehension:
l = [i for i in l if i%n]
writing if i%n here is the same as writing if i%n != 0, and n divides i iff i%n==0
Methods:
Using a generator
One of the first option that comes to mind is to make use of a generator. The generator would iterate through a sequence, and test if the current element is divisible by n. This allows you to have a more generic solution as well:
def filter_by_multiple(seq, n):
for i in seq:
if i % n:
yield i
Usage:
>>> filter_by_multiple([2, 3, 4, 5, 6, 7, 8, 9, 10], 2)
<generator object filter_by_multiple at 0x000000374ED30258>
>>> list(filter_by_multiple([2, 3, 4, 5, 6, 7, 8, 9, 10], 2))
[3, 5, 7, 9]
>>>
Using a generator expression
While the above solution is fine, is can be shortened even more by using generator expressions. generator expression are like list comprehensions, but unlike them, they return a generator iterator instead of a list.
Usage:
>>> l = [2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(el for el in l if el % 2)
[3, 5, 7, 9]
>>>
Using filter():
Among many of the builtin functions in Python, there is one for filtering list called filter(). The usually way to use filter() is to pass in the function you want to use to filter your list, and then the actual list you want filtered. In your case, you want to filter out every element the is not a multiple of two:
Usage:
>>> l = [2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(filter(lambda x: x % 2, l))
[3, 5, 7, 9]
>>>
Using a list comprehension
While all of the above are fine ways for filtering a list, probably the most obvious and canonical, is to use a list comprehension. In your case, your list comprehension, is dead simple.
Usage:
>>> l = [2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> [el for el in l if el % 2]
[3, 5, 7, 9]
>>>
This question already has answers here:
How to get the cartesian product of multiple lists
(17 answers)
Closed 9 years ago.
I am currently using python to execute my code. My input into this function is a list within a list like [ [1,2,3],[4,5],[6,7,8,9] ] The goal is to iterate and create all the possible combinations for each list.
The only way I have it in my mind is to do multiple for loops on each sublist within the master list. Each combination must have at least one element from each sublist so since there are 3 sublists, all combinations must have 3 elements.
for a in [1,2,3]:
for b in [4,5]:
for c in [6,7,8,9]:
l.append([a,b,c])
So one combination would be [1,4,6],[1,4,7],[1,4,8],[1,4,9]. And the next loop with be [1,5,6]..[1,5,7]...and so forth.
This works but my problem is that I don't know how many sublists will be in the master list (input) so I cant just keep writing for loops indefinitely. I know there must be a way to write a recursive function but I have never done this and don't know how it works. I don't think it should be too difficult but can someone show me the algorithm to accomplish this please?
Thank you so much!!
If you are trying to learn about recursion, think this way: The combinations of all the lists consist of taking one item at a time from the first list and prepending this item to all the combinations of the remaining lists.
This way you get
def combinations(rest, prefix = [], result = []):
if rest:
first = rest.pop()
for item in first:
prefix.append(item)
combinations(rest, prefix, result)
prefix.pop()
rest.append(first)
else:
result.append(list(reversed(prefix)))
return result
NB I am far from a Python expert. It's likely there is a more elegant way to code this. Note that because I'm pushing and popping at the ends of lists I need to reverse the final results. The advantage of this is that it's fast and should produce no garbage.
In Idle:
>>> combinations([ [1,2,3],[4,5],[6,7,8,9] ] )
[[1, 4, 6], [2, 4, 6], [3, 4, 6], [1, 5, 6], [2, 5, 6], [3, 5, 6],
[1, 4, 7], [2, 4, 7], [3, 4, 7], [1, 5, 7], [2, 5, 7], [3, 5, 7],
[1, 4, 8], [2, 4, 8], [3, 4, 8], [1, 5, 8], [2, 5, 8], [3, 5, 8],
[1, 4, 9], [2, 4, 9], [3, 4, 9], [1, 5, 9], [2, 5, 9], [3, 5, 9]]
Try using the itertools library:
import itertools
arr = [[1,2], [3,4], [5,6]]
list(itertools.product(*arr))
# => [(1, 3, 5), (1, 3, 6), (1, 4, 5), (1, 4, 6), (2, 3, 5), (2, 3, 6), (2, 4, 5), (2, 4, 6)]
This question already has answers here:
How do I make a flat list out of a list of lists?
(34 answers)
Closed 9 years ago.
I have one list like:
n = [[1, 2, 3], [4, 5, 6, 7, 8, 9]]
I want to create a function that takes a single list (see above) and concatenates all the sublists that are part of it into a single list.
n = [[1, 2, 3], [4, 5, 6, 7, 8, 9]]
nn = [ x for y in n for x in y]
>>> lst = [[1, 2, 3], [4, 5, 6, 7, 8, 9]]
>>> from itertools import chain
>>> list(chain.from_iterable(lst))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
For completeness, here is a very short way to write this
>>> sum(n, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
But although it is tempting, you shouldn't because it has quadratic performance. ie a new list is created as each term is added, and all the previous items will be copied over and over
It's ok to use list.extend though
reduce(lambda x,y: x.extend(y) or x, n, [])
You can also concatenate by doing simply:
print n[0]+n[1]
In general this would be:
def concatenate(list):
x=[]
for i in list:
x+=i
return x
But this is not particularly efficent, just quite straightforward for a beginner.
My Python code generates a list everytime it loops:
list = np.genfromtxt('temp.txt', usecols=3, dtype=[('floatname','float')], skip_header=1)
But I want to save each one - I need a list of lists right?
So I tried:
list[i] = np.genfromtxt('temp.txt', usecols=3, dtype=[('floatname','float')], skip_header=1)
But Python now tells me that "list" is not defined. I'm not sure how I go about defining it. Also, is a list of lists the same as an array??
Thank you!
You want to create an empty list, then append the created list to it. This will give you the list of lists. Example:
>>> l = []
>>> l.append([1,2,3])
>>> l.append([4,5,6])
>>> l
[[1, 2, 3], [4, 5, 6]]
Create your list before your loop, else it will be created at each loop.
>>> list1 = []
>>> for i in range(10) :
... list1.append( range(i,10) )
...
>>> list1
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9], [2, 3, 4, 5, 6, 7, 8, 9], [3, 4, 5, 6, 7, 8, 9], [4, 5, 6, 7, 8, 9], [5, 6, 7, 8, 9], [6, 7, 8, 9], [7, 8, 9], [8, 9], [9]]
Use append method, eg:
lst = []
line = np.genfromtxt('temp.txt', usecols=3, dtype=[('floatname','float')], skip_header=1)
lst.append(line)
First of all do not use list as a variable name- that is a builtin function.
I'm not super clear of what you're asking (a little more context would help), but maybe this is helpful-
my_list = []
my_list.append(np.genfromtxt('temp.txt', usecols=3, dtype=[('floatname','float')], skip_header=1))
my_list.append(np.genfromtxt('temp2.txt', usecols=3, dtype=[('floatname','float')], skip_header=1))
That will create a list (a type of mutable array in python) called my_list with the output of the np.getfromtext() method in the first 2 indexes.
The first can be referenced with my_list[0] and the second with my_list[1]
Just came across the same issue today...
In order to create a list of lists you will have firstly to store your data, array, or other type of variable into a list. Then, create a new empty list and append to it the lists that you just created. At the end you should end up with a list of lists:
list_1=data_1.tolist()
list_2=data_2.tolist()
listoflists = []
listoflists.append(list_1)
listoflists.append(list_2)