Explain list comprehension in cumulative sum of list - python

Python code to get the Cumulative sum of a list
def Cumulative(lists):
cu_list = []
length = len(lists)
cu_list = [sum(lists[0:x:1]) for x in range(0, length+1)]
return cu_list[1:]
# Driver Code
lists = [10, 20, 30, 40, 50]
print (Cumulative(lists))
Can anyone help me to expand above list comprehension ?
I have written this code
def cum_sum(m):
result = 0
for x in m:
result += x
print(result, end=", ")
cum_sum([1,2,3,10])

This is the expanded form of the list comprehension:
cu_list = []
for x in range(0, length+1):
s = sum(lists[0:x:1])
cu_list.append(s)

use a generator
def cum_sum(m):
result = 0
for x in m:
result += x
yield result
list(cum_sum([1,2,3,10]))

ans = []
for i in range(len(lists)):
ans.append(sum(lists[:i+1]))

this part of your code
[sum(lists[0:x:1]) for x in range(0, length+1)]
creates a new list starting from the first element of lists up to (and excluding) the x'th element of your list (see e.g. Python List Slicing). then it takes the sum over it.
so it unnecessarily creates several lists and does unnecessary sums (it could keep the sum up to element x in memory and just add the next element - as do most solutions presented here).
you get that for free with itertools.accumulate already:
from itertools import accumulate
lists = [10, 20, 30, 40, 50]
print(list(accumulate(lists)))
# [10, 30, 60, 100, 150]

Related

Adding a variable copying the one before and the one after for a long list

I have 2 different lists that I would like to modify:
The first one goes like so :
['chelsea','manchester','london','liverpool',..]
The list is quite long and goes on for a while.
I would like to get it like so :
['chelsea','chelsea-manchester','manchester','london','london-liverpool','liverpool',...]
and so on.
The second one is one with lots of numbers in
['10','11','12','13','14','15','16','17','18','19','20','21'..]
which also goes on for a long time.
I want to modify the list to get rid of the 4th and 5th variables for each block of 5 variables like so :
['10','11','12','15','16','17','20','21','22']
Many thanks,
This is what you are looking for according to your post.
1st List
data = ['chelsea','manchester','london','liverpool']
new_data = [[data[i*2],f'{data[i*2]}-{data[i*2+1]}',data[i*2+1]] for i in range(int(len(data)/2))]
sum(new_data,[])
['chelsea', 'chelsea-manchester', 'manchester', 'london', 'london-liverpool', 'liverpool']
2nd List
number = ['10','11','12','13','14','15','16','17','18','19','20','21','22']
number = [int(i) for i in number]
new_number = [number[i:i+3] for i in range(0,len(number),5)]
sum(new_number,[])
[10, 11, 12, 15, 16, 17, 20, 21, 22]
data = ['chelsea','manchester','london','liverpool']
data_connect = [[data[0]]] + [[f"{data[i-1]}-{data[i]}", data[i]] for i, x in enumerate(data[1:])]
data_rs = [y for x in data_connect for y in x]
data = ['10','11','12','13','14','15','16','17','18','19','20','21']
# split data(list) as length 5
data_split = [data[i:i + length] for i in range(0, len(data), 5)]
# remove 4th and 5th index value from each list
data_rs = [x for d in data_split for x in d[:3]]

Check if the elements of a range are within a list of lists

I have a sorted list and a range contains multiple lists:
>>> n= [10, 20, 30, 40]
>>> m= [[1, 20], [21, 30]]
What I am trying to do is to check if all the elements of the n list are within either of the existing ranges in m or not. For instance, from the above example, 40 is not within any of the ranges.
I tried to extend the answer to the question in the following post, but seems it is not working.
Checking if all elements of a List of Lists are in another List of Lists Python
is_there = set(tuple(x) for x in [n]).issubset(tuple(x) for x in m)
You should go through each element in n and check if it's in the range of each list of m. Assuming you are only working with ints:
[any(x in range(r[0], r[1]) for r in m) for x in n]
If you want to include the end of your range, just add 1:
[any(x in range(r[0], r[1]+1) for r in m) for x in n]
The simple approach is to check all the elements:
items = [10, 20, 30, 40]
ranges = [[1, 20], [21, 30]]
result = all(any(low <= i <= high for low, high in ranges) for i in items)
For fun, you can make the containment check a bit different by using actual range objects:
range_objects = [range(low, high + 1) for low, high in ranges]
filtered_items = all(any(i in r for r in range_objects) for i in items)
If you wanted to get the matching items:
good = [i for i in items if any(low <= i <= high for low, high in ranges)]
You could also get the bad elements instead:
bad = [i for i in items if all(i < low or i > high for low, high in ranges)]
That way, your original result is just not bad.
Since you said, "a sorted list", you can use the following logic of min and max. The outside will be True if any of the elements in n will be outside the given ranges. It will be False, if none of the elements is outside the ranges
n= [10, 20, 30, 40] # < as per you, this is sorted
m= [[1,20], [21,30]]
outside = any([(min(n) < i[0] and max(n)> i[1]) for i in m])
# True
Edit Answering the test case asked by #Peter DeGlopper in the comment below
m = [[1, 20], [31, 40]]
n = [10, 20, 25, 30, 40]
outside = any([(l < i < r for i in n) for l, r in m])
# True

Squaring a list, then amending entire thing it into an empty list

I'm being asked to square a set list of integers, and cube a set list of integers and floating numbers, then amend each of those lists into two separate empty lists.
I'm using python on jupyter. I'm being limited to the things we've already learned (important point - I've tried using functions we haven't learned yet and the professor would prefer I stay confined to topics we've covered). We've learned to make lists, measure the length of our lists, amend to our lists, and for loops (using rang)e and while loops... the very-basics.
x = [2,4,6,8,10,12,14,16,18]
y = [10,8.25,7.5,7,6.5,7,7.5,8.25,10]
# initialize new lists below
xsquared = []
ycubed = []
# loop(s) to compute x-squared and y-cubed below
for item_X in x:
item_X **= 2
for item_Y in y:
item_Y **= 3
# Use .append() to add computed values to lists you initialized
xsquared.append(item_X)
print(xsquared)
ycubed.append(item_Y)
print(ycubed)
# Results
Actual results:
[324]
[1000]
Expected results:
[4, 16, 36, 64, 100.... 324]
[1000, 561.515625, 421.875.... 1000]
Using list comprehension, you can do this as:
x_squared = [item_x**2 for item_x in x]
y_cubed = [item_y**3 for item_y in y]
You are only appending the last result. If you want to stick with the topics you have covered, you should use for loops:
x = [2,4,6,8,10,12,14,16,18]
y = [10,8.25,7.5,7,6.5,7,7.5,8.25,10]
xsquared = []
ycubed = []
for item_X in x:
xsquared.append(item_X ** 2)
for item_Y in y:
ycubed.append(item_Y ** 3)
However, the simplest way is to use a list comprehension:
x = [2,4,6,8,10,12,14,16,18]
y = [10,8.25,7.5,7,6.5,7,7.5,8.25,10]
xsquared = [n ** 2 for n in x]
ycubed = [n ** 3 for n in x]
The output in both cases:
print(xsquared)
print(ycubed)
[4, 16, 36, 64, 100, 144, 196, 256, 324]
[1000, 561.515625, 421.875, 343, 274.625, 343, 421.875, 561.515625, 1000]
if you want to avoid list comprehensions or map()
x = [2,4,6,8,10,12,14,16,18]
y = [10,8.25,7.5,7,6.5,7,7.5,8.25,10]
x2 = []
y3 = []
for i in x:
x2.append(i*i)
for i in y:
y3.append(i**3)

add neigbouring elements in a list

How can I write a function which takes a list of integers and creates a new list with the same number of elements as the original list such that each integer in the new list is the sum of its neighbours and itself in the original list. For example, if a_list = [10, 20, 30, 40, 50], the new_list = [30, 60, 90, 120, 90].
I tried a list comprehension;
def sum_neighbours(a_list):
b_list = a_list
li = [x+y for x in a_list for y in b_list]
return li
print(sum_neighbours([10, 20, 30, 40, 50]))
Which gave me;
[20, 30, 40, 50, 60, 30, 40, 50, 60, 70, 40, 50,
60, 70, 80, 50, 60, 70, 80, 90, 60, 70, 80, 90, 100]
I then tried a for loop;
def sum_neighbours(a_list):
element1 = a_list[0]
new_list = []
for element in a_list:
element1 += element
new_list += element1
return new_list
print(sum_neighbours([10,20,30,40,50]))
Which threw a TypeError: 'int' object is not iterable
But in both cases I don't think my ideas/attempts are on the right track... I would really appreciate any help, hints, tips that more experienced people may have.
You can start with a solution that iterates through the indexes of the list, like so:
def sum_neighbours1(a_list):
result = []
for i in range(len(a_list)):
if i == 0:
result.append(a_list[i] + a_list[i+1])
elif i == len(a_list)-1:
result.append(a_list[i-1] + a_list[i])
else:
result.append(a_list[i-1] + a_list[i] + a_list[i+1])
return result
print(sum_neighbours1([10,20,30,40,50]))
Or, you can build three distinct lists: the original list, the list of left-hand neighbors, and the list of right-hand neighbors. We have to put 0 at the front or rear of the auxiliary lists. Each of the lists must be at least as long as the original list.
Zip the three lists together, and add them:
def sum_neighbours2(a_list):
priors = [0] + a_list
subsequents = a_list[1:] + [0]
return map(sum, zip(priors, a_list, subsequents))
Note that I used the functional notation map(sum, data), but I could have used a list conprehension just as easily:
def sum_neighbours3(a_list):
priors = [0] + a_list
subsequents = a_list[1:] + [0]
return [p+a+s for p,a,s in zip(priors, a_list, subsequents)]
And, if I were eager to make the result a single expression (for example, if it had to fit in a lambda expression), here that is:
def sum_neighbours4(a_list):
return [
left + a + right
for left, a, right in zip(
[0] + a_list,
a_list,
a_list[1:] + [0])
]
Here is my one line solution with zip :
def sum_neighbours(a) :
return [sum(x) for x in [(a[0],a[1])]+zip(a,a[1:],a[2:])+[(a[-2],a[-1])]]
print sum_neighbours([10, 20, 30, 40, 50]) # display [30, 60, 90, 120, 90]
with :
a, the original list
zip(a,a[1:],a[2:]), 3-tuples generation
[(a[0],a[1])] and [(a[-2],a[-1])], 2-tuples for particular cases (first and last numbers of list)
You'll probably want a for i in range loop, and indexing directly into the array so you can look at the element that is at i+1 and i-1 indexes (making sure to check that your +/- 1 isn't going outside the bounds of the array).

Efficient & Pythonic way of finding all possible sublists of a list in given range and the minimum product after multipying all elements in them?

I've achived these two things.
Find all possible sublists of a list in given range (i ,j).
A = [ 44, 55, 66, 77, 88, 99, 11, 22, 33 ]
Let, i = 2 and j = 4
Then, Possible sublists of the list "A" in the given range (2,4) is :
[66], [66,77], [66,77,88], [77], [77,88], [88]
And, minimum of the resultant product after multipying all the elements of the sublists:
So, the resultant list after multiplying all the elements in the above sublists will become
X = [66, 5082, 447216, 77, 6776, 88]`
Now, the minimum of the above list, which is min(X) i.e 66
My Code:
i, j = 2, 4
A = [ 44, 55, 66, 77, 88, 99, 11, 22, 33 ]
O, P = i, i
mini = A[O]
while O <= j and P <= j:
if O == P:
mini = min(mini, reduce(lambda x, y: x * y, [A[O]]))
else:
mini = min(mini, reduce(lambda x, y: x * y, A[O:P + 1]))
P += 1
if P > j:
O += 1
P = O
print(mini)
My Question:
This code is taking more time to get executed for the Larger Lists and Larger Ranges ! Is there any possible "Pythonic" way of reducing the time complexity of the above code ? Thanks in advance !
EDIT :
Got it. But, If there is more than one such possible sublist with the same minimum product,
I need the longest sub list range (i,j)
If there are still more than one sublists with the same "longest sub range", I need to print the sub-interval which has the lowest start index.
Consider this list A = [2, 22, 10, 12, 2] if (i,j) = (0,4).
There is a tie. Min product = 2 with two possibilities '(0,0)' and '(4,4)' . Both sub list range = 0 [ (0-0) and (4-4) ]
In this case i need to print (minproduct, [sublist-range]) = 2, [0,0]
Tried using dictionaries, It works for some inputs but not for all ! How to do this 'efficiently' ?
Thank you !
First, given the list and the index range, we can get the sublist A[i : j + 1]
[66, 77, 88]
For positive integers a and b, a * b is no less than a or b. So you don't need to do multiplying, it's not possible that multiplying of two or more elements has a smaller result. The minimum of this list is the minimum of all the multiplying results.
So the result is:
min(A[i : j + 1])
For generating the sublists, it is as simple as two nested for loops in a list comprehension:
def sublists(l,i,j):
return [l[m:n+1] for m in range(i,j+1) for n in range(m,j+1)]
example:
>>> sublists(A,2,4)
[[66], [66, 77], [66, 77, 88], [77], [77, 88], [88]]
For finding the minimum product:
>>> min(map(prod, sublists(A,2,4)))
66
(you import prod from numpy, or define it as def prod(x): return reduce(lambda i,j:i*j,x))
The accepted answer is correct for all positive ints as you cannot multiply the smallest element by any number and get a smaller result. It might make more sense if you were getting all the slices greater than length 1.
If you were going to calculate it then you could use itertools.islice to get each slice and get the min using a generator expression:
from itertools import islice
from operator import mul
print(min(reduce(mul, islice(A, n, k + 1), 1)
for n in range(i, j + 1) for k in range(n, j + 1)))
66
If for i = 0 and j = 4 you considered (44, 55, 66, 88) a legitimate slice then you would need to use itertools.combinations.
#EDIT: Quick Solution:
min(A[i:j+1])
Since all the numbers are positive integers, and you want to find the minimum product of all possible sublists of A[i:j+1] list
slice, it will also contain sublists of length 1. The minimum products of all such sublists will be lowest number among the A[i:j+1] slice.
Another Solution:
The below method will be useful when you need to find the maximum product of sublists or you need all the possible combinations of A[i:j+1] list slice.
We'll use itertools.combinations to solve this. We can do this in 3 steps.
Step1: Get the slice of the list
my_list = A[i:j+1]
This will give us the slice to work on.
my_list = A[2:5]
my_list
[66, 77, 88]
Step-2 Generate all possible combinations:
import itertools
my_combinations = []
for x in range(1, len(my_list)+1):
my_combinations.extend(list(itertools.combinations(my_list,x)))
my_combinations
[(66,), (77,), (88,), (66, 77), (66, 88), (77, 88), (66, 77, 88)]
iterools.combinations returns r length subsequences of elements from
the input iterable
So, we will use this to generate subsequences of length 1 to length equal to length of my_list. We will get a list of tuples with each element being a subsequence.
Step-3 : Find min product of all possible combinations
products_list = [reduce(lambda i,j:i*j, x) for x in my_combinations]
[66, 77, 88, 5082, 5808, 6776, 447216]
min(products_list)
66
After getting the subsequences, we apply list comprehension along with reduce() to get the list of products for all the subsequences in my_combinations list. Then we apply min() function to get the minimum product out of the products_list which will give us our answer.
Take a look a itertools.combinations()
https://docs.python.org/3/library/itertools.html#itertools.combinations
Call it passing the sublist, in a loop, with the other parameter varying from 1 to the length of the sublist.
It will definitely take "more time to get executed for the Larger Lists and Larger Ranges", i think that's inevitable. But might be much faster than your approach. Measure and see.
def solution(a_list):
sub = [[]]
for i in range(len(a_list)):
for j in range(len(a_list)):
if(i == j):
sub.append([a_list[i]])
elif(i > j):
sub.append([a_list[j],a_list[i]])
sub.append(a_list)
return sub
solution([10, 20, 30])
[[], [10], [10, 20], [20], [10, 30], [20, 30], [30], [10, 20, 30]]

Categories