I have a list (fullList) [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15] and a value n, - I would like to iterate the first (n) 4 values, then skip 1 number, then iterate the next 4-1 value, and skip 2 numbers, then iterate the next 4-2 numbers and skip 3 numbers, etc... until the list is fully iterated.
My final answer should look like this:
[5,8,9,10,9,10,11,13,14,15]
It can be obtained by taking the first 4 values (5,8,9,10), then skipping 1 count (5), then take the next 3 values (9,10,11) and skipping 2 counts (8,9), then taking the next 2 values (13,14) and skipping 3 counts (9,10,13), taking the next value (15) and skipping 4 counts (10,11,14,15)
--
Edit: I have obtained fullList from iterating the values of a smaller list (listb) [2,3,6,7,8] by adding it against itself. I have solved this part, but still would like to understand the first part (above).
I would like to achieve the following results:
2+3 = 5
2+6 = 8
2+7 = 9
2+8 = 10
3+6 = 9
3+7 = 10
3+8 = 11
6+7 = 13
6+8 = 14
7+8 = 15
Thank you!
You can use itertools.chain
from itertools import chain
x = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
x = list(chain.from_iterable(x[4*idx + idx:4*idx + 4] for idx in range(4)))
For your second part, use itertools.combinations
from itertools import combinations
x = [2,3,6,7,8]
for combo in combinations(x, 2):
a, b = combo
print(f"{a} + {b} = {sum(combo)}")
Using a nested list comprehension:
from itertools import chain
full_list = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
n = 4
list(chain.from_iterable(x[i:] for i,x in enumerate(
[x for x in [full_list[i*n:i*n+n] for i in range(n+1)]])))
# [5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
The nested list comprehension:
[x for x in [full_list[i*n:i*n+n] for i in range(n+1)]]
groups the list into sublists of n elements. The outer list comprehension then extracts from those sublists, further sublists of the required length using enumerate() to determine the start offset.
Finally itertools.chain.from_iterable() is used to flatten the sublists into a single list as required.
Well, I made it in 2 different ways:
The first one iterates list element by element with 2 separate counters, the skip counter and the array number counter (counter), when the counter gets fill to the n number (counter == n), it gets into a for loop and starts poping out of the main list the elements (skipping the future iterations as you told us you want to do), when the counterSkip gets to 0, it resets all the counters, reduces the n variable by 1 and increments the number of numbers you want to skip by one (skipN) until the array is full iterated
counter = 0
n= 4
skipN = 1
counterSkip = skipN
array = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
for index in enumerate(array):
if counter >= n and counterSkip != 0:
for i in range(counterSkip):
array.pop(index[0])
counterSkip -= 1
n -= 1
skipN += 1
counter = 0
counterSkip = skipN
counter += 1
print(array)
#
[5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
And the second way (It's almost identical as one answer here):
Using the extend() method to add elements to a new list setting lower bound of the main list to a var named "index" that updates it's value summing itself to the n var +1 to preserve a continuity in the iteration, and the upper bound of the main list to the index itself minus i, that serves as a accumulator of how many elements you want to skip each iteration.
You can read more about the "extend" method right here: https://www.programiz.com/python-programming/methods/list/extend
newList = []
index = 0
i = 0
n= 4
list = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
while(index<len(list)):
newList.extend(list[index:index+(n-i)])
index+=(n+1)
i+=1
print(newList)
#
[5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
Edit: Ignore the second/alternative part of the question - I managed to get the list that I was looking for
lista = [2,3,6,7,8]
count = 0
originalCount = 0
fullList = []
while count < len(lista):
for j in range(len(lista)):
if (count != j):
if (j > count):
fullList.append(lista[count]+lista[j])
elif (count != originalCount):
j+=count
count+=1
OK, since this seems to have turned into a kind of golfing contest, here's mine:
def upper_left(arr, n):
"""Arguments:
arr is an iterator returning a flattened n x n array.
Since no element is selected from the last row, it can be
just for the first n-1 rows.
Returns:
An iterator which produces the flattened upper-left triangle
of arr, excluding the anti-diagonal.
"""
return (z for z, (i, j) in zip(arr, ((i,j)
for i in range(n)
for j in range(n)))
if i + j < n - 1)
print(list(upper_left(
[
5, 8, 9, 10, 5,
9, 10, 11, 8, 9,
13, 14, 9, 10, 13,
15, 10, 11, 14, 15
], 5)))
This zips the array against the corresponding row/column indices, and then selects the elements for which the indices are above and to the left of the anti-diagonal (that is, the diagonal from the lower-left corner to the upper-right corner).
Running the above file produces:
$ python upper_left.py
[5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
It would arguably be easier to combine the selection of elements with the generation of the elements in the first place. But this function composes well. (It's probably worth writing the functions which produce the other triangles. The only difference is the comparison in the last line of the function.
Personally I think using itertools makes it more complex than it needs to be.
This solution should work.
new_list = []
index = 0
i = 0
while(index<len(fullList)):
new_list.extend(fullList[index:index+(n-i)])
index+=(n+1)
i+=1
Edit
fullList = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
new_list = []
index = 0
i = 0
n = 4
while(index<len(fullList)):
new_list.extend(fullList[index:index+(n-i)])
index+=(n+1)
i+=1
print(new_list)
#[5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
Related
Let's say I have a dataset
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
I am trying to write a for loop that will sum the third and fourth numbers in the dataset, then subtract the sum of the first and second numbers in the dataset, then move to the next set of four (so sum the fourth and fifth, then subtract the sum of the second and third from that) down the line until it gets to the last number with three other numbers ahead of it. Problem is, I have no idea how to do either of those things. Here's what I have:
for n in a:
runsum = ((a[3] + a[2]) - (a[0] + a[1])) ... ((a[15] + a[14]) - (a[13] + a[12]))
print(runsum)
Obviously, "..." isn't how I let the for loop know it should move down the dataset, but I'm not sure how so I'm using it as a placeholder. Any advice would be much appreciated.
I think this is what you want:
[(w+z)-(x+y) for x,y,z,w in (a[i:i+4] for i in range(0,len(a),4))]
Which evaluates to [4, 4, 4, 4]
Loop through every 4th position:
for i in range(0,len(a),4):
print((a[i+3]+a[i+2])-(a[i]+a[i+1]))
The straightforward answer to this question would be to use for loop and sum all required indices:
s = 0
for i in range(0, len(a) - 3):
s += a[i+2] + a[i+3] - a[i] - a[i+1]
Or, as a for comprehension:
sum(a[i+2] + a[i+3] - a[i] - a[i+1] for i in range(0, len(a) - 3))
However, if you consider elements that are being added and removed to the sum, you'll notice that most of the elements do not participate in the result, as they are being added and removed the same number of times.
For example, for indices in the range [0..15] here are indices that are added:
[2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15]
And these indices are subtracted:
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13]
You can see that there is overlap in the middle, and some difference at the start and at the end.
You can exploit that and only sum this difference:
s = -sum(a[:3]) - a[1] + sum(a[-3:]) + a[-2]
This adds last three elements (element before the last twice), and subtracts first three elements (second element twice). This yields the same result as the for loop.
Try this:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
fragments = [a[i:i + 4] for i in range(0, len(a), 4)]
result = sum([(f[2] + f[1]) - (f[0] + f[1]) for f in fragments])
print(result)
# 8
a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
sum = 0
for i in range(1,int(len(a)/4)+1 ):
sum = sum + ((a[i*4 -1] + a[i*4 -2]) - (a[i*4 -4] + a[i*4 -3]))
You can use the sum function and striding subscripts stepping by 4:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
r = sum(a[2::4])+sum(a[3::4]) - sum(a[::4]) - sum(a[1::4])
print(r) #16
Each subscript pick up the following indexes:
Indexes: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
a[2::4) 3 7 11 15 sum=36
a[3::4] 4 8 12 16 sum=40 = 76
a[::4] 1 5 9 13 sum=28
a[1::4] 2 6 10 14 sum=32 - 60
----
16
alternatively you can subtract 2x the negative part from the whole sum
r = sum(a) - 2*( sum(a[::4]) + sum(a[1::4]) )
Another way to do it would be to add from a comprehension where you take the negative for the 1st/2nd of each group of 4
r = sum((x,-x)[i%4<2] for i,x in enumerate(a))
With numpy:
import numpy as np
# some sample data
np.random.seed(0)
a = np.random.randint(0, 10, 16)
print(a)
It gives:
[5 0 3 3 7 9 3 5 2 4 7 6 8 8 1 6]
Then compute differences between sums of consecutive numbers:
out = a[2:-1] + a[3:] - a[:-3] - a[1:-2]
print(out)
The result:
[ 1 7 10 2 -8 -5 -2 4 7 3 3 -5 -9]
Alternatively, this gives the same result:
out = np.convolve([1, 1, -1, -1], a, 'valid')
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]
Suppose i have the following tuple as:
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
What i want to do is reversing certain parts of the tuple.
For example 4 elements stays same and then 4 elements will be reversed.
I want to get the following result:
a = (1,2,3,4,8,7,6,5,9,10,11,12,16,15,14,13,17,18,19,20)
How can i achieve that (as pythonian kind of way)?
Thanks...
Tuples are immutable, however, by converting a to a list, in-place assignment can be performed:
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
new_a = list(a)
for c, i in enumerate(range(0, len(a), 4)):
if c%2:
new_a[i:i+4] = new_a[i:i+4][::-1]
print(tuple(new_a))
Output:
(1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12, 16, 15, 14, 13, 17, 18, 19, 20)
The direct, simple, readable answer (pythonic?):
a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
for n in range(0, len(a), 8):
a[n+4:n+8] = a[n+7:n+3:-1]
print(a)
Here's a generator-based solution. The benefits of doing it this way is that this solution doesn't require the input to be sliceable, so you can apply it to the output of functions like zip and map.
from itertools import zip_longest as zipl
from itertools import cycle, chain
_marker = object()
def cycle_map(iterable, func_iterable):
funcs = cycle(func_iterable)
for func, item in zip(funcs, iterable):
if func:
yield func(item)
else:
yield item
def reverse_filter(iterable, remove=_marker):
t = tuple(i for i in iterable if i is not remove)
return reversed(t)
def reverse_alternating(iterable, length=4):
chunks = zipl(*[iter(iterable)]*length, fillvalue=_marker)
funcs = (None, reverse_filter)
return chain.from_iterable(cycle_map(chunks, funcs))
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
list(reverse_alternating(a))
# [1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12, 16, 15, 14, 13, 17, 18, 19, 20]
Using list slicing.
Ex:
from itertools import chain
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
res = [a[v:v+4] if i%2 == 0 else list(reversed(a[v:v+4])) for i, v in enumerate(range(0, len(a), 4))]
print(tuple(chain.from_iterable(res))) #Flatten list
Output:
(1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12, 16, 15, 14, 13, 17, 18, 19, 20)
Just another way to approach the problem using list comprehension (not the fastest probably). Wanted to share the logic, assuming that len(a) is divisible by 4:
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
lst = [a[j:j+4] if i%2 == 0 else a[j:j+4][::-1] for i, j in
enumerate(range(0,len(a), 4))]
result = tuple([j for i in lst for j in i])
print (result)
Output
(1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12, 16, 15, 14, 13, 17, 18, 19, 20)
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]]
I am building a list of integers that should increment by 2 alternating values.
For example, starting at 0 and alternating between 4 and 2 up to 20 would make:
[0,4,6,10,12,16,18]
range and xrange only accept a single integer for the increment value. What's the simplest way to do this?
I might use a simple itertools.cycle to cycle through the steps:
from itertools import cycle
def fancy_range(start, stop, steps=(1,)):
steps = cycle(steps)
val = start
while val < stop:
yield val
val += next(steps)
You'd call it like so:
>>> list(fancy_range(0, 20, (4, 2)))
[0, 4, 6, 10, 12, 16, 18]
The advantage here is that is scales to an arbitrary number of steps quite nicely (though I can't really think of a good use for that at the moment -- But perhaps you can).
You can use a list comprehension and the modulus operator to do clever things like that. For example:
>>> [3*i + i%2 for i in range(10)]
[0, 4, 6, 10, 12, 16, 18, 22, 24, 28]
l = []
a = 0
for i in xrnage (N) :
a += 2
if i&1 == 0 :
a+=2
l.append (a)
Looks simple enough to me.
This could be solution that is flexible and work for any range.
def custom_range(first, second, range_limit):
start , end = range_limit
step = first + second
a = range(start, end, step)
b = range(first, end, step)
from itertools import izip_longest
print [j for i in izip_longest(a,b) for j in i if j!=None]
custom_range(4,2,(0,19))
custom_range(6,5,(0,34))
Output:
[0, 4, 6, 10, 12, 16, 18]
[0, 6, 11, 17, 22, 28, 33]
1 Generate a range of numbers from 0 to n with step size 4
2 generate another range of numbers from 0 to n with step size 6
3 Combine both the list and sorted. Remove duplicates
>>> a = range(0,20,4)
>>> a
[0, 4, 8, 12, 16]
>>> b = range(0,20,6)
>>> c = sorted(a + b)
>>> b
[0, 6, 12, 18]
>>> c
[0, 0, 4, 6, 8, 12, 12, 16, 18]
>>> c = list(set(c))
>>> c
[0, 4, 6, 8, 12, 16, 18]