Python: Sum part of a list - python

Given a list, how can I find sums of part of the list? For example:
a = [2,5,4,6,8]
sum(a) = 25
I want to find out if a part of the list sums up to a certain number.
The list part should have a certain length. This is my goal:
ex_list = [2,5,4,6,8]
dif_list = partsum(ex_list, 3)
print(dif_list) ==> [11, 13, 15, 12, 14, 16, 15, 17, 19, 18]
Each element in 'dif_list' is given by taking 3 numbers out of 'ex_list' and summing them up, i.e. 2+5+4 = 11, 2+5+6 = 13, 2+5+8 = 15, 2+4+6 = 12, etc.
Also for reference:
ex_list = [2,5,4,6,8]
another_list = partsum(ex_list, 4)
print(another_list) ==> [17, 19, 21, 20, 23]
Because 2+5+4+6 = 17, 2+5+4+8 = 19, etc.
Basically, partsum(thing, num) will take 'num' items of 'thing', sum them up, and append it to a new list. All help is appreciated, thanks!

You want itertools.combinations.
import itertools
lst = [2,5,4,6,8]
combos = itertools.combinations(lst, 3)
# combos is equivalent to
# [ [2, 5, 4], [2, 5, 6], [2, 5, 8],
# [2, 4, 6], [2, 4, 8], [2, 6, 8],
# [5, 4, 6], [5, 4, 8], [5, 6, 8],
# [4, 6, 8] ]
result = [sum(combo) for combo in combos]

Related

(Python 3) Splitting a list using unique elements as separators

I have a list l=[2,3,4,5,6,99,7,8,9,10,11,12,100,13,14,15,16,17,18,101] and I will like to split l into sublists [2,3,4,5,6], [7,8,9,10,11,12],[13,14,15,16,17,18], meaning that I use 'separator' digits 99,100,101 that belong to separators = [99,100,101] as a flag to indicate where I should go on to the next list.
In particular, these sublists may not have the same number of elements, but are different in size of only 1 (5 or 6). Note: There may be more than 3 separators.
Is there an efficient way to do this in Python 3? I thought of first extracting the indices of the separator elements and then slice the list accordingly but it seems far too complex and computationally intensive..
Some insight will be great!
Add on (suggestion from #Netwave): My attempt (which clearly does not work):
g = []
for i in l:
if i in separators:
g += [l[:l.index(i)]]
Output:
>>> g
[[2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 99, 7, 8, 9, 10, 11, 12], [2, 3, 4, 5, 6, 99, 7, 8, 9, 10, 11, 12, 100, 13, 14, 15, 16, 17, 18]]
Use groupby:
from itertools import groupby
separators = [99, 100, 101]
l = [2, 3, 4, 5, 6, 99, 7, 8, 9, 10, 11, 12, 100, 13, 14, 15, 16, 17, 18, 101]
splits = [list(values) for key, values in groupby(l, key=lambda x: x not in separators) if key]
print(splits)
Output
[[2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17, 18]]
I hope you are looking for something similar to the below code. You need to iterate over your list and keep checking if the element is present in the separator list. This can be done other way around, as you said by iterating over the separator list and finding the index of the elements in the main list. For the sake of simplicity I took the former approach. (Make a note of the use of endindex variable):
l=[2,3,4,5,6,99,7,8,9,10,11,12,100,13,14,15,16,17,18,101]
seperator = [99,100,101]
list_of_list = []
endindex = 0
for i in range(0,len(l),1):
if l[i] in seperator:
list_of_list.append(l[endindex:i])
endindex = i + 1
print(list_of_list)
Ouput:
[[2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17, 18]]
Easier for use in a function:
import itertools
def split(l,l2):
return [list(v) for k,v in itertools.groupby(l,lambda x: x in l2) if not k]
l = [2, 3, 4, 5, 6, 99, 7, 8, 9, 10, 11, 12, 100, 13, 14, 15, 16, 17, 18, 101]
print(split(l,[99, 100, 101]))
Output:
[[2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17, 18]]
Realized a duplicate of Split a list into nested lists on a value

How to generate a 2D list which will contain numbers from 1..n without using numpy?

The 2D list should be like this:
matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
]
I am unable to find the logic for this. I have done this using numpy's reshape function. But unable to do without numpy.
Here is one simple way:
res = [list(range(i, i+4)) for i in range(1, 14, 4)]
print(res)
[[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]]
You can also wrap in a generic function:
def square(n):
return [list(range(i, i+n)) for i in range(1, n**2, n)]
res = square(4)
Explanation
The syntax for range construction is range(start, end, [step]). step is optional; if it is not specified, it is assumed to be 1.
The first part range(i, i+n) creates a range object from i to i+n-1, inclusive.
The second part range(1, n**2, n) iterates in steps of n to n*n, not including the final term. Since end is non-inclusive, squaring n provides the desired cap.
This is one approach
l = range(1, 17) #Create a list using range
print([l[i:i+4] for i in range(0, len(l), 4)]) #Divide the list into 4 equal chunks
Output:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
I like this one too:
>> [[i+4*j for i in range(1,5)] for j in range(4)]
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
Use a list-comprehension like this with tuple unpacking:
>>> [[*range(i, i+4)] for i in range(1, 14, 4)]
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
or you could use the grouper from the itertools recipes to split a given list (or iterable) into an 2d matrix. this is something along those lines:
def to2d(items, width):
return list(list(i) for i in zip(*(iter(items_1d),) * width))
items_1d = list(range(1, 17)) # or just items_1d = range(1, 17)
print(to2d(items=items_1d, width=4))

Replacing a vertical sublist in a list of lists

This question is an extension to this question.
I'm representing a two-dimensional array using list of lists, L, say:
[ [1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4] ]
For a given sub-list, say [9, 99], I want to replace a specific sub-list in the "2-D" list by this sublist using something intuitive like:
L[1][0:2] = sublist
# which updates `L` to:
[ [1, 2, 3, 4],
[1, 9, 99, 4],
[1, 2, 3, 4],
[1, 2, 3, 4] ] # not in this format, but written like this for clarity
This works for horizontal replacements, but not for vertical replacements since, as we can't slice to separate lists like this: L[0:2][0]. If I had to use this slicing system, I could transpose L (Transpose list of lists), then use this slicing method, then transpose it back. But that's not efficient, even for the sake of simplicity.
What would be an efficient way to replicate L[0:2][0] and get this output?
[ [1, 2, 3, 4],
[1, 9, 3, 4],
[1, 99, 3, 4],
[1, 2, 3, 4] ]
Note: Assume len(sublist) <= len(L), for vertical replacements (which is the focus of this question).
Looping approach:
def replaceVert(al : list, repl:list, oIdx:int, iIdx:int):
for pos in range(len(repl)):
al[oIdx+pos][iIdx] = repl[pos]
a = [ [1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16] ]
print(a) # [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
replaceVert(a,['ä','ü'],2,2) # this is a one liner ;)
print(a) # [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 'ä', 12], [13, 14, 'ü', 16]]
Transpose/slice/transpose approach:
I overread the mentioning of "no transposing". This is using transpose, change, transpose method with slicing which is not wanted by the Q. It is a answer for the title of this question, so I decided to leave it in for future people search SO and stumble over this Q:
a = [ [1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16] ]
b = list(map(list,zip(*a))) # will make [ [1,5,9,13], ... ,[4,8,12,16]]
b[1][0:2]=['a','b'] # replaces what you want here (using a and b for clarity)
c = list(map(list,zip(*b))) # inverts b back to a's form
print(a)
print(b)
print(c)
Output:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]] # a
[[1, 5, 9, 13], ['a', 'b', 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]] # b replaced
[[1, 'a', 3, 4], [5, 'b', 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]] # c
Timing 4x4 list, 2 replaces:
setuptxt = """
def replaceVert(al : list, repl:list, oIdx:int, iIdx:int):
for pos in range(len(repl)):
al[oIdx+pos][iIdx] = repl[pos]
a = [ [1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16] ]
"""
zipp = """b = list(map(list,zip(*a)))
b[1][0:2]=['a','b']
c = list(map(list,zip(*b)))
"""
import timeit
print(timeit.timeit("replaceVert(a,['ä','ü'],2,2)",setup = setuptxt))
print(timeit.timeit(stmt=zipp, setup=setuptxt))
Output:
looping: 12.450226907037592
zipping: 7.50479947070815
The method wit ZIPPing (transpose/slice/transpose) needs roughly 60% of the time for 4x4 lists.
Bigger list 1000x1000 and ~70 elements replaced:
setuptxt = """
def replaceVert(al : list, repl:list, oIdx:int, iIdx:int):
for pos in range(len(repl)):
al[oIdx+pos][iIdx] = repl[pos]
a = [ [kk for kk in range(1+pp,1000+pp)] for pp in range(1,1000)]
repl = [chr(mm) for mm in range(32,100)]
"""
import timeit
print(timeit.timeit("replaceVert(a,repl,20,5)",number=500, setup = setuptxt))
zipp = """b = list(map(list,zip(*a)))
b[20][5:5+len(repl)]=repl
c = list(map(list,zip(*b)))
"""
print(timeit.timeit(stmt=zipp, setup=setuptxt,number=500))
Output:
looping: 0.07702917579216137
zipping: 69.4807168493871
Looping wins. Thanks #Sphinx for his comment

Reverse certain elements in a 2d array to produce a matrix in the specified format, Python 3

I have the following code for a list of lists with the intention of creating a matrix of numbers:
grid=[[1,2,3,4,5,6,7],[8,9,10,11,12],[13,14,15,16,17],[18,19,20,21,22]]
On using the following code which i figured out would reverse the list, it produces a matrix ...
for i in reversed(grid):
print(i)
The output is:
[18, 19, 20, 21, 22]
[13, 14, 15, 16, 17]
[8, 9, 10, 11, 12]
[1, 2, 3, 4, 5, 6, 7]
I want however, the output to be as below, so that the numbers "connect" as they go up:
[22,21,20,19,18]
[13,14,15,16,17]
[12,11,10,9,8]
[1,2,3,4,5,6,7]
Also, for an upvote, I'd be interested in more efficient ways of generating the matrix in the first place. For instance, to generate a 7x7 array - can it be done using a variable, for instance 7, or 49. Or for a 10x10 matrix, 10, or 100?
UPDATE:
Yes, sorry - the sublists should all be of the same size. Typo above
UPDATE BASED ON ANSWER BELOW
These two lines:
>>> grid=[[1,2,3,4,5,6,7],[8,9,10,11,12],[13,14,15,16,17],[18,18,20,21,22]]
>>> [lst[::-1] for lst in grid[::-1]]
produce the following output:
[[22, 21, 20, 18, 18], [17, 16, 15, 14, 13], [12, 11, 10, 9, 8], [7, 6, 5, 4, 3, 2, 1]]
but I want them to print one line after the other, like a matrix ....also, so I can check the output is as I specified. That's all I need essentially, for the answer to be the answer!
You need to reverse the list and also the sub-lists:
[lst[::-1] for lst in grid[::-1]]
Note that lst[::-1] reverses the list via list slicing, see here.
You can visualize the resulting nested lists across multiples lines with pprint:
>>> from pprint import pprint
>>> pprint([lst[::-1] for lst in grid[::-1]])
[[22, 21, 20, 19, 18],
[17, 16, 15, 14, 13],
[12, 11, 10, 9, 8],
[7, 6, 5, 4, 3, 2, 1]]
usually 2D matrices are created, manipulated with numpy
then index slicing can reorder rows, columns
import numpy as np
def SnakeMatrx(n):
Sq, Sq.shape = np.arange(n * n), (n, n) # Sq matrix filled with a range
Sq[1::2,:] = Sq[1::2,::-1] # reverse odd row's columns
return Sq[::-1,:] + 1 # reverse order of rows, add 1 to every entry
SnakeMatrx(5)
Out[33]:
array([[21, 22, 23, 24, 25],
[20, 19, 18, 17, 16],
[11, 12, 13, 14, 15],
[10, 9, 8, 7, 6],
[ 1, 2, 3, 4, 5]])
SnakeMatrx(4)
Out[34]:
array([[16, 15, 14, 13],
[ 9, 10, 11, 12],
[ 8, 7, 6, 5],
[ 1, 2, 3, 4]])
if you really want a list of lists:
SnakeMatrx(4).tolist()
Out[39]: [[16, 15, 14, 13], [9, 10, 11, 12], [8, 7, 6, 5], [1, 2, 3, 4]]
numpy is popular but not a official Standard Library in Python distributions
of course it can be done with list manipulation
def SnakeLoL(n):
Sq = [[1 + i + n * j for i in range(n)] for j in range(n)] # Sq LoL filled with a range
for row in Sq[1::2]:
row.reverse() # reverse odd row's columns
return Sq[::-1][:] # reverse order of rows
# or maybe more Pythonic for return Sq[::-1][:]
# Sq.reverse() # reverse order of rows
# return Sq
SnakeLoL(4)
Out[91]: [[16, 15, 14, 13], [9, 10, 11, 12], [8, 7, 6, 5], [1, 2, 3, 4]]
SnakeLoL(5)
Out[92]:
[[21, 22, 23, 24, 25],
[20, 19, 18, 17, 16],
[11, 12, 13, 14, 15],
[10, 9, 8, 7, 6],
[1, 2, 3, 4, 5]]
print(*SnakeLoL(4), sep='\n')
[16, 15, 14, 13]
[9, 10, 11, 12]
[8, 7, 6, 5]
[1, 2, 3, 4]
Simple way of python:
list(map(lambda i: print(i), [lst[::-1] for lst in grid[::-1]]))

Splitting list based on missing numbers in a sequence

I am looking for the most pythonic way of splitting a list of numbers into smaller lists based on a number missing in the sequence. For example, if the initial list was:
seq1 = [1, 2, 3, 4, 6, 7, 8, 9, 10]
the function would yield:
[[1, 2, 3, 4], [6, 7, 8, 9, 10]]
or
seq2 = [1, 2, 4, 5, 6, 8, 9, 10]
would result in:
[[1, 2], [4, 5, 6], [8, 9, 10]]
Python 3 version of the code from the old Python documentation:
>>> # Find runs of consecutive numbers using groupby. The key to the solution
>>> # is differencing with a range so that consecutive numbers all appear in
>>> # same group.
>>> from itertools import groupby
>>> from operator import itemgetter
>>> data = [ 1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28]
>>> for k, g in groupby(enumerate(data), lambda i_x: i_x[0] - i_x[1]):
... print(list(map(itemgetter(1), g)))
...
[1]
[4, 5, 6]
[10]
[15, 16, 17, 18]
[22]
[25, 26, 27, 28]
The groupby function from the itertools module generates a break every time the key function changes its return value. The trick is that the return value is the number in the list minus the position of the element in the list. This difference changes when there is a gap in the numbers.
The itemgetter function is from the operator module, you'll have to import this and the itertools module for this example to work.
Alternatively, as a list comprehension:
>>> [map(itemgetter(1), g) for k, g in groupby(enumerate(seq2), lambda i_x: i_x[0] - i_x[1])]
[[1, 2], [4, 5, 6], [8, 9, 10]]
This is a solution that works in Python 3 (based on previous answers that work in python 2 only).
>>> from operator import itemgetter
>>> from itertools import *
>>> groups = []
>>> for k, g in groupby(enumerate(seq2), lambda x: x[0]-x[1]):
>>> groups.append(list(map(itemgetter(1), g)))
...
>>> print(groups)
[[1, 2], [4, 5, 6], [8, 9, 10]]
or as a list comprehension
>>> [list(map(itemgetter(1), g)) for k, g in groupby(enumerate(seq2), lambda x: x[0]-x[1])]
[[1, 2], [4, 5, 6], [8, 9, 10]]
Changes were needed because
Removal of tuple parameter unpacking PEP 3113
map returning an iterator instead of a list
Another option which doesn't need itertools etc.:
>>> data = [1, 4, 5, 6, 10, 15, 16, 17, 18, 22, 25, 26, 27, 28]
>>> spl = [0]+[i for i in range(1,len(data)) if data[i]-data[i-1]>1]+[None]
>>> [data[b:e] for (b, e) in [(spl[i-1],spl[i]) for i in range(1,len(spl))]]
... [[1], [4, 5, 6], [10], [15, 16, 17, 18], [22], [25, 26, 27, 28]]
I like this one better because it doesn't require any extra libraries or special treatment for first case:
a = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 20, 21, 22]
b = []
subList = []
prev_n = -1
for n in a:
if prev_n+1 != n: # end of previous subList and beginning of next
if subList: # if subList already has elements
b.append(subList)
subList = []
subList.append(n)
prev_n = n
if subList:
b.append(subList)
print a
print b
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 20, 21, 22]
[[1, 2, 3, 4, 5, 6, 7, 8], [10, 11, 12], [15, 16, 17, 18], [20, 21, 22]]
My way
alist = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 20, 21, 22]
newlist = []
start = 0
end = 0
for index,value in enumerate(alist):
if index < len(alist)-1:
if alist[index+1]> value+1:
end = index +1
newlist.append(alist[start:end])
start = end
else:
newlist.append(alist[start: len(alist)])
print(newlist)
Result
[[1, 2, 3, 4, 5, 6, 7, 8], [10, 11, 12], [15, 16, 17, 18], [20, 21, 22]]

Categories

Resources