I have the following code that (nearly) populates a list of lists (I will call it a 2d array) in Python.
Instead of going up from 0-6 and repeating this 3 times, I want it to populate the array with numbers 0 - 20. Please see code below:
#matrix=[1,2,3,4,5,6,7],[8,9,10,11,12,13,14],[15,16,17,18,19,20,21]
#print(matrix[1][2])
rows=3
columns=7
for i in range(rows):
for j in range(columns):
matrix=[j]
i=i+1
print(matrix,end="")
The erroneous output is:
[0][1][2][3][4][5][6][0][1][2][3][4][5][6][0][1][2][3][4][5][6]
I want the output to be:
[1,2,3,4,5,6,7],[8,9,10,11,12,13,14],[15,16,17,18,19,20,21]
There are fancier ways, but this is the most straightforward:
>>> rows = 3
>>> columns = 7
>>> n = 1
>>> matrix = []
>>> for _ in range(rows):
... sub = []
... for _ in range(columns):
... sub.append(n)
... n += 1
... matrix.append(sub)
...
>>> matrix
[[1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14], [15, 16, 17, 18, 19, 20, 21]]
And for good measure, a fancy way:
>>> import itertools
>>> counter = itertools.count(1)
>>> rows = 3
>>> columns = 7
>>> matrix = [[n for n, _ in zip(counter, range(columns))] for _ in range(rows)]
>>> matrix
[[1, 2, 3, 4, 5, 6, 7], [9, 10, 11, 12, 13, 14, 15], [17, 18, 19, 20, 21, 22, 23]]
>>>
Use list comprehension. You want 3 rows so the base of the list comprehension is: [for y in range(rows)]. You want to have incrementing numbers starting with a number divisible by columns but starting from 1 so: range(columns*y+1,...) and you want to have columns range (7) so range(columns*y+1,columns+(columns*y+1)) and then turn that into a list.
rows=3
columns=7
matrix=[list(range(columns*y+1,columns+(columns*y+1))) for y in range(rows)]
print(matrix)
#outputs: [[1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14], [15, 16, 17, 18, 19, 20, 21]]
Related
I have a list of elements and want to get the sum of this list:
a = [4, 5, "X", "X"].
But X can be 2 different values, 2 or 3. So there are 4 sums of this list:
sum1 = sum([4, 5, 2, 2]) = 13
sum2 = sum([4, 5, 2, 3]) = 14
sum3 = sum([4, 5, 3, 2]) = 14
sum4 = sum([4, 5, 3, 3]) = 15
Basically I want to get a tuple (or list) of all possible sums of the list, like:
sums = (13, 14, 14, 15)
For an input list with 0 X, I want to get a tuple with 1 element, for a tuple with 1 X a tuple of 2 elements, with 2 X tuple with 4 elements...with n X a tuple of 2^n elements.
Similar to Michael's answer, also using itertools.product, but with a dictionary mapping elements to possible values. This might be more flexible, allowing placeholders in all positions and different values for different placeholders, and also makes the code a bit simpler IMHO.
>>> from itertools import product
>>> a = [4, 5, "X", "X"]
>>> vals = {"X": [2, 3]}
>>> [sum(p) for p in product(*[vals.get(x, [x]) for x in a])]
[13, 14, 14, 15]
You can use itertools.product and a list comprehension
from itertools import product
a = [4, 5, "X", "X"]
r = a.count('X')
[sum(a[:len(a)-r] + [*i]) for i in product([2,3], repeat=r)]
Output
[13, 14, 14, 15]
Testing with more cases
#test cases
for n in range(5):
a=[4,5, *['X']*n] # [4,5] .... [4,5,'X','X','X','X']
r = a.count('X')
print([sum(a[:len(a)-r] + [*i]) for i in product([2,3], repeat=r)])
Output
[9]
[11, 12]
[13, 14, 14, 15]
[15, 16, 16, 17, 16, 17, 17, 18]
[17, 18, 18, 19, 18, 19, 19, 20, 18, 19, 19, 20, 19, 20, 20, 21]
Consider two lists of identical length:
t is a list of irregular-intervals of times in seconds, arranged chronologically
pt is a list of sequences of numbers 1,2,3 wsuch that a 1 is followed by a consecutive string of 2's, then followed by a 3.
1 = start of event, 2 = continuation of event, 3 = end of event
This means that for a single event, the sequence begins with a single 1, is followed by a consecutive string of 2s (how many times it repeats will vary), and finally ends in a single 3.
There is more than 1 event contained in this vector
For example, the input could look like:
# |--Event #1-| |---Event #2----| |Event #3 |
pt = [1, 2, 2, 3, 1, 2, 2, 2, 3, 1, 2, 3 ]
t = [1, 10, 13, 14, 17, 20, 21, 25, 37, 32, 33, 38]
Is there a 1-liner that doesn't involve multiple nested loops that we could use that would calculate the difference in time values in t for each event sequence in pt?
For example, the desired output for the above inputs would be a list of length 3 (because there are 3 events) where the output is
Output: [13, 20, 6]
### Explanation:
# 13 = 14-1 = t[position where pt shows first 3] - t[position where pt shows first 1]
# 20 = 37-17 = t[position where pt shows second 3] - t[position where pt shows second 1]
# 6 = 38-32 = t[position where pt shows third 3] - t[position where pt shows third 1]
using pure python:
pt = [1, 2, 2, 3, 1, 2, 2, 2, 3, 1, 2, 3 ]
t = [1, 10, 13, 14, 17, 20, 21, 25, 37, 32, 33, 38]
l = [y for x, y in zip(pt,t) if x in [1,3]]
print([l[i:i+2][1] - l[i:i+2][0] for i in range(0, len(l), 2)])
[13, 20, 6]
using more_itertools.chunked():
from more_itertools import chunked
print([y-x for x,y in chunked([y for x, y in zip(pt,t) if x in [1,3]], 2)])
[13, 20, 6]
explanation
If you look close, we see this list comprehension occurring multiple times. This is the center of the solution!
[y for x, y in zip(pt,t) if x in [1,3]]
So, what's going on?
Using the zip function, we make a list of the paired elements, and if the x element (1st list element pair) is either 1 or 3 we add it to the list.
This gives us a list of the differences we need to find.
#|---| |----| |----|
[1, 14, 17, 37, 32, 38]
Now comes the second part, getting the differences from these. We essentially need to make pairs from this, the method I'm going to use here is chunking. The pure python way to partition a list into chunks is as follows:
#given a list l
chunklen = 2
[l[i:i+chunklen] for i in range(0, len(l), chunklen)]
using this we could partition the [1, 14, 17, 37, 32, 38] list to:
[[1, 14], [17, 37], [32, 38]]
but it's far simpler to just get the differences immediately!
l[i:i+chunklen][1]-l[i:i+chunklen][0]
#given l[i:i+chunklen] as [1, 14] this would return 14-1 i.e. 13
This seems to work
pt = [1, 2, 2, 3, 1, 2, 2, 2, 3, 1, 2, 3 ]
t = [1, 10, 13, 14, 17, 20, 21, 25, 37, 32, 33, 38]
st = 0
lst = []
for x in zip(pt,t):
if x[0] == 1: st = x[1]
if x[0] == 3:
d = x[1] - st
lst.append(d)
print(lst)
Output
[13, 20, 6]
Code:
pt = [1, 2, 2, 3, 1, 2, 2, 2, 3, 1, 2, 3 ]
t = [1, 10, 13, 14, 17, 20, 21, 25, 37, 32, 33, 38]
events_t =[]
arr = []
for pt_ele, t_ele in zip(pt, t):
arr.append(t_ele)
if pt_ele == 3:
events_t.append(arr)
arr = []
print(events_t)
res = [i[-1] - i[0] for i in events_t]
print(res)
Output:
[[1, 10, 13, 14], [17, 20, 21, 25, 37], [32, 33, 38]]
[13, 20, 6]
input:
a sorted list, like this:[1,2,3,8,10,15,16,17,18,22,23,27,30,31]
a threshold, like this: max_diff = 2
expected output:
a list of sub lists; each sub list contains the values that the neighboring difference is smaller than max_diff, like this: [[1, 2, 3], [8, 10], [15, 16, 17, 18], [22, 23], [27], [30, 31]]
Here's how I did this, I am wondering if there is a better way to do this.
test_list = [1,2,3,8,10,15,16,17,18,22,23,27,30,31]
max_diff = 2
splited_list = []
temp_list = [test_list[0]]
for i in xrange(1,len(test_list)):
if test_list[i] - temp_list[-1] > max_diff:
splited_list.append(temp_list)
temp_list = [test_list[i]]
else:
temp_list.append(test_list[i])
if i == len(test_list) -1:
splited_list.append(temp_list)
print splited_list
You can use enumerate and zip function within a list comprehension to find the indices of the elements that value difference is larger than 2, then split your list based on index list :
>>> li =[1, 2, 3, 8, 10, 15, 16, 17, 18, 22, 23, 27, 30, 31]
>>> inds=[0]+[ind for ind,(i,j) in enumerate(zip(li,li[1:]),1) if j-i>2]+[len(li)+1]
>>> [li[i:j] for i,j in zip(inds,inds[1:])]
[[1, 2, 3], [8, 10], [15, 16, 17, 18], [22, 23], [27], [30, 31]]
>>> a = [1,2,3,8,10,15,16,17,18,22,23,27,30,31]
>>> b = a[1:] #offset by 1 position
>>> b
[2, 3, 8, 10, 15, 16, 17, 18, 22, 23, 27, 30, 31]
>>> c = [(i[1] - i[0]) for i in zip(a[:-1], b)]
>>> c #position diff
[1, 1, 5, 2, 5, 1, 1, 1, 4, 1, 4, 3, 1]
>>> d = [i[0] for i in enumerate(c) if i[1] > 2]
>>> d #split position
[2, 4, 8, 10, 11]
>>> e = [-1]+d+[len(a)]
>>> e #add start end to split position
[-1, 2, 4, 8, 10, 11, 14]
>>> [a[l[0]+1: l[1]+1] for l in zip(e, e[1:])]
[[1, 2, 3], [8, 10], [15, 16, 17, 18], [22, 23], [27], [30, 31]]
#split result
Rearranging your lines leads to a more compact form:
test_list = [1,2,3,8,10,15,16,17,18,22,23,27,30,31]
max_diff = 2
splited_list = []
prev_element = float('-inf')
for element in test_list:
if element - prev_element > max_diff:
splited_list.append([])
splited_list[-1].append(element)
prev_element = element
print splited_list
Works on all iterables
def split_by_threshold(seq, max_diff=2):
it = iter(seq)
last = next(it)
part = [last]
for curr in it:
if curr - last > max_diff:
yield part
part = []
part.append(curr)
last = curr
yield part
l = [1,2,3,8,10,15,16,17,18,22,23,27,30,31]
print(list(split_by_threshold(l)))
I'm totally new with Python; however, I would like to make a list of 6 sub-lists, each of which contains 4 random values between 0 and 30.
The answer should print like this:
lst[0] = [1,4,4,5]
lst[1] = [23,14,5,8]
...
lst[5] = [8,9,18,27]
Here is the simple one-liner that'll get it done:
from random import randint
rand_list = [[randint(0, 30) for i in range(4)] for n in range(6)]
>>>rand_list
[[28, 17, 5, 9],
[27, 29, 3, 8],
[18, 13, 4, 11],
[15, 12, 11, 19],
[17, 8, 22, 30],
[24, 6, 9, 1]]
If you want to get fancy, you can easily expand it into a function that will allow you to specify the list-sizes as inputs to the function:
def rand_lists(num_lists, num_vals):
return [[randint(0, 30) for i in range(num_vals)] for n in range(num_lists)]
>>> rand_lists(2, 7)
[[17, 8, 29, 2, 9, 26, 16],
[24, 22, 5, 10, 11, 0, 10]]
For a one-liner:
from random import randint
lst = [[randint(0, 30) for col in range(4)] for row in range(6)]
Which is equivalent to(in pure python):
lst = []
for row in range(6):
r = []
lst.append(r)
for col in range(4):
r.append(randint(0, 30))
In Python, you can get the numbers in a range by calling range(x,y). But given two ranges, say 5-15, and 10-20 how can you get all the numbers 5-20 without duplicates? The ranges may also be disjoint.
I could concat all the results and then uniquify the list, but is that the fastest solution?
>>> a = range(5, 15)
>>> b = range(10, 20)
>>> print sorted(set(a + b))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Or if you want a more general expansion of the lists to their elements for inclusion in the set:
>>> list_of_lists = [a, b]
>>> print sorted(set(elem for l in list_of_lists for elem in l))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
And I found a way to compose it all in one line:
>>> list_of_lists = [a, b]
>>> print set.union(*map(set, list_of_lists))
Is sorting necessary? It's just expository, but then I don't see that sets necessarily output in sorted order:
>>> x = set(range(3))
>>> x
set([0, 1, 2])
>>> x.add(-1)
>>> x
set([0, 1, 2, -1])
Or you can join overlapping ranges:
>>> def join_overlapping_ranges(ranges):
... list_of_ranges = []
... # ranges are sorted on first element
... for r in sorted(ranges):
... # ranges are stored as [start, end]
... if list_of_ranges and list_of_ranges[-1][1] >= r[0]:
... list_of_ranges[-1][1] = r[1]
... else:
... list_of_ranges.append(r)
... return list_of_ranges
...
>>> ranges = [[3,4], [5,7], [1,2], [4,6], [5,5]]
>>> print sorted(ranges)
[[1, 2], [3, 4], [4, 6], [5, 5], [5, 7]]
>>> print join_overlapping_ranges(ranges)
[[1, 2], [3, 7]]
Sort the ranges (x, y) by increasing x values. Now, for each range, if it overlaps the previous range, set your current "big range"'s y value to the current range's y value. If it doesn't, start a new "big range": this one won't overlap any of the previous ones. If the current range is completely included in the current big one, ignore it.
For the quantity you need I would just keep it simple
>>> a = range(5, 15)
>>> b = range(10, 20)
>>> from itertools import chain
>>> sorted(set(chain.from_iterable(list_of_lists)))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]