Python: how to create sublists through loop? - python

I have a long list containing elements that value from 0 to 100.
What I want to do is find all the positions where my elements take on values [0,2]. Then find out all the positions of the values between 2 and 4, etc up to 98 and 100.
Let's call the list containing the values ''list''. And let's call the resulting list p_x. Where x indicates which interval we are finding the positions of.
I managed to get what i want this way:
p_61 = N.where((list >= 60) & (list <= 62))
My question now is: how do i loop this, so that i get as a result all the p_x's that I want?

import itertools
l = list(range(10))
## If you just want the values:
print([(x,y) for (x,y) in itertools.combinations(l, 2) if abs(x-y)==2])
## If you want the positions:
for i, x in enumerate(l):
for j, y in enumerate(l):
if j <= i:
continue
if abs(x-y) == 2:
print(i, j)
## If you want them stored in lists in a dictionary:
d = {}
for i, x in enumerate(l):
for j, y in enumerate(l):
if j <= i:
continue
if abs(x-y) == 2:
k = 'p_{}'.format(x+1)
try:
d[k].append((i,j))
except KeyError:
d[k] = [(i,j)]

Related

How to combine two or more lists by adding each element [duplicate]

This question already has answers here:
How to sum columns of an array in Python
(13 answers)
Closed 4 years ago.
I have lists that contain only integers like [1,2,3] and [4,5,6]. Some times I even have [7,8,9] or more lists. How do I add each elements together to form a new list of the same length?
[1,2,3] + [4,5,6] + [7,8,9] = [12,15,18]
I know that the above would just append the elements and create a longer list (with 9 elements), but instead I would like to add the lists element-wise.
You can put the lists in a list, zip the sub-lists after unpacking them with the * operator, and map the sum to a list:
l = [[1,2,3], [4,5,6], [7,8,9]]
print(list(map(sum, zip(*l))))
This outputs:
[12, 15, 18]
Edit: The above is for Python 3.x. If you're using an earlier version, you can use itertools.izip instead of zip, and you would not need to call the list constructor:
import itertools
l = [[1,2,3], [4,5,6], [7,8,9]]
print map(sum, itertools.izip(*l))
Numpy is well suited for this task, although it might be slight overkill for your case.
Let's say you have
x = [1,2,3]
y = [4,5,6]
z = [7,8,9]
You can turn them into an array and sum across them:
np.array([x, y, z]).sum(axis=0)
You could use a similar approach with regular lists and zip:
[sum(col) for col in zip(x, y, z)]
You can do it like this:
#Create the list
x = [1,2,3,12,13]
y = [4,5,7,11]
z = [7,8,9,14,15,16]
#create a list with the list length
lenList = [len(x),len(y),len(z)]
finalList = []
#get the minimum and maximum list length
minLen = min(lenList)
maxLen = max(lenList)
#if the three list have the same length then you can simply sum the element
if(maxLen == minLen):
for j in range(maxLen):
finalList.append(x[j]+y[j]+z[j])
else:
#if the list have different length look for the minimum value
while(lenList[0] != lenList[1] or lenList[0] != lenList[2]):
minLen = min(lenList)
maxLen = max(lenList)
#if the min len is the x list then add some zero
if(lenList.index(minLen) == 0):
#change the len to the max len
lenList[0] = maxLen
for a in range(minLen,maxLen):
x.append(0)
#if the min len is the y list then add some zero
elif(lenList.index(minLen) == 1):
lenList[1] = maxLen
for b in range(minLen,maxLen):
y.append(0)
#if the min len is the z list then add some zero
elif(lenList.index(minLen) == 2):
lenList[2] = maxLen
for c in range(minLen,maxLen):
z.append(0)
#sum all the element
for j in range(0,maxLen):
finalList.append(x[j]+y[j]+z[j])
print finalList
output:
[12, 15, 19, 37, 28, 16]
Comments:
This program can sum all the list element and insert the result in another list. You can sum 3 list, with infinite element and without having the same list length. If you need to sum more list just edit this part:
while(lenList[0] != lenList[1] or lenList[0] != lenList[2]):
minLen = min(lenList)
maxLen = max(lenList)
#if the min len is the x list then add some zero
if(lenList.index(minLen) == 0):
#change the len to the max len
lenList[0] = maxLen
for a in range(minLen,maxLen):
x.append(0)
#if the min len is the y list then add some zero
elif(lenList.index(minLen) == 1):
lenList[1] = maxLen
for b in range(minLen,maxLen):
y.append(0)
#if the min len is the z list then add some zero
elif(lenList.index(minLen) == 2):
lenList[2] = maxLen
for c in range(minLen,maxLen):
z.append(0)
Here you can add:
elif(lenList.index(minLen) == 3):
lenList[3] = maxLen
for c in range(minLen,maxLen):
listN.append(0)
After that you have to add the list:
#Create the list
x = [1,2,3,12,13]
y = [4,5,7,11]
z = [7,8,9,14,15,16]
listN = [0,0,0]
#create a list with the list length
lenList = [len(x),len(y),len(z),len(listN)]
And in the sum iteration you have to change:
#sum all the element
for j in range(0,maxLen):
finalList.append(x[j]+y[j]+z[j],listN[j])
an alternative method if you know the length of the sub lists are 3
a = [1,2,3] + [4,5,6] + [7,8,9]
[sum([a[3*q+i] for q in range(len(a)//3)]) for i in range(len(a)//3)]
output
[12,15,18]
alternatively
dicta = {q:i for q,i in enumerate(a)}
[sum([dicta[3*q+i] for q in range(len(a)//3)]) for i in range(len(a)//3)]
you can just swap the 3's out with what ever length you use, but since you showed in your code that they where already laid to gether, i wouldn't be able to read it automaticly. What goes for the alternative solution, then dictionaries uses hashes so if your list gets big it won't be a problem.

Checking loop in Python

I have this question about loops.
Imagine You have this data array:
list = [1,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1]
How would You write a loop which checks if the previous number is lower, and the next after the checked one (Condition looks like this [5,6,5]). So the loop will get to number 9 and print it or save it, whatever.
Using next with a generator expression:
lst = [1,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1]
res = next(j for i, j, k in zip(lst, lst[1:], lst[2:]) if i < j and i == k)
If you need all such numbers, use a list comprehension instead:
res = [j for i, j, k in zip(lst, lst[1:], lst[2:]) if i < j and i == k]
If you need a condition that will show all numbers that are higher than their previous and next ones:
lst = [1,2,3,4,3,2,3,1,2,1,2,3,4,5,6,7,8,6]
res = [j for i, j, k in zip(lst, lst[1:], lst[2:]) if i < j > k]
[4, 3, 2, 8] is printed.
Explanation
You can iterate the list with shifted versions of itself via zip.
For each triplet, test your two conditions.
Use next to extract such triplets; if no such triplet exists, you will meet StopIteration error.
Never name a variable after a built-in, e.g. use lst instead of list.
You could just write a simple loop that checks the previous number is less than the current number, and the next number is equal to the previous number:
lst = [1,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1]
for i in range(len(lst)):
if lst[i-1] < lst[i] and lst[i-1] == lst[i+1]:
print(lst[i])
# 9

If numbers in list are equal to n, print out their indices

The Task:
You are given two parameters, an array and a number. For all the numbers that make n in pairs of two, return the sum of their indices.
input is: arr = [1, 4, 2, 3, 0, 5] and n = 7
output: 11
since the perfect pairs are (4,3) and (2,5) with indices 1 + 3 + 2 + 5 = 11
So far I have this, which prints out the perfect pairs
from itertools import combinations
def pairwise(arr, n):
for i in combinations(arr, 2): # for index in combinations in arr, 2 elements
if i[0] + i[1] == n: # if their sum is equal to n
print(i[0],i[1])
Output:
4,3 2,5
However does anyone has tips on how to print the indices of the perfect pairs? Should I use numpy or should I change the whole function?
Instead of generating combinations of array elements you can generate combinations of indices.
from itertools import combinations
def pairwise(arr, n):
s = 0
for i in combinations(range(len(arr)), 2): # for index in combinations in arr, 2 elements
if arr[i[0]] + arr[i[1]] == n: # if their sum is equal to n
# print(arr[i[0]],arr[i[1]])
# print(i[0],i[1])
s += i[0] + i[1]
# print(s)
return s
You can use a dictonary mapping the indexes:
def pairwise(arr, n):
d = {b:a for a,b in enumerate(arr)} #create indexed dict
for i in combinations(arr, 2): # for index in combinations in arr, 2 elements
if i[0] + i[1] == n: # if their sum is equal to n
print(d[i[0]],d[i[1]])
Here you have a live example
Rather than generating combinations and checking if they add up to n, it's faster to turn your list into a dict where you can look up the exact number you need to add up to n. For each number x you can easily calculate n - x and then look up the index of that number in your dict.
This only works if the input list doesn't contain any duplicate numbers.
arr = [1, 4, 2, 3, 0, 5]
n = 7
indices = {x: i for i, x in enumerate(arr)}
total = 0
for i, x in enumerate(arr):
remainder = n - x
if remainder in indices:
idx = indices[remainder]
total += i + idx
# the loop counts each pair twice (once as [a,b] and once as [b,a]), so
# we have to divide the result by two to get the correct value
total //= 2
print(total) # output: 11
If the input does contain duplicate numbers, you have rewrite the code to store more than one index in the dict:
import collections
arr = [1, 4, 2, 3, 0, 5, 2]
n = 7
indices = collections.defaultdict(list)
for i, x in enumerate(arr):
indices[x].append(i)
total = 0
for i, x in enumerate(arr):
remainder = n - x
for idx in indices[remainder]:
total += i + idx
# the loop counts each pair twice (once as [a,b] and once as [b,a]), so
# we have to divide the result by two to get the correct value
total //= 2
You should use the naive approach here:
process each element of the array with its indice
for each element test for all elements after this one (to avoid duplications) whether their sum is the expected number and if it is add the sum of their indices
Code could be:
def myfunc(arr, number):
tot = 0
for i, val in enumerate(arr):
for j in range(i+1, len(arr)):
if val + arr[j] == number:
tot += i + j
return tot
Control:
>>> myfunc([1, 4, 2, 3, 0, 5], 7)
11
>>> myfunc([2, 4, 6], 8)
2

Conversion of iterative algorithm to recursive

Hi I am trying to convert my iterative algorithm to recursive solution to achieve Dynamic Programming after it's done (Do suggest me other ways to reduce time complexity of this triple nested iteration). I am not good with recursion. I had tried to convert it but it is giving me index out of range errors.
Iterative Approach:
def foo(L):
L=sorted(L)
A = 0
for i,x in enumerate(L):
for j,y in enumerate(L):
if x != y:
if (y%x ==0):
for k,z in enumerate(L):
if y != z:
if (z%y==0) and (y%x==0):
A= A+1
return A
Recursive Approach:
A =i =j= k =0 #Initializing globals
def foo(L):
L=sorted(L)
global A ,i,j,k
x=y=z=L
luckyOne(x,y,z)
return A
def luckyOne(x,y,z):
global i,j,k
while(i< len(x) and j < len(y) and k < len(z)):
while(x[i] != y[j]):
luckyTwo(x[i:],y[j:],z[k:])
i+=1
luckyOne(x[i:],y[j:],z[k:])
# i+=1
# return A
i+=1
luckyOne(x[i:],y[j:],z[k:])
return 0
def luckyTwo(x,y,z):
global i,j,k
while (i< len(x) and j < len(y) and k < len(z)):
while(y[j]%x[i]==0):
luckyThree(x[i:],y[j:],z[k:])
j+=1
luckyTwo(x[i:],y[j:],z[k:])
j+=1
luckyTwo(x[i:],y[j:],z[k:])
return 0
def luckyThree(x,y,z):
global A ,i,j,k
while (i< len(x) and j < len(y) and k < len(z)):
while (y[j]!=z[k]):
while((z[k]%y[j]==0) and (y[j]%x[i]==0)):
A+=1
print 'idr aya'
k+=1
luckyThree(x[i:],y[j:],z[k:])
k+=1
luckyThree(x[i:],y[j:],z[k:])
return 0
The input should be like L=['1','2','3']
This is the fastest version I can come up with:
def foo(lst):
edges = {x: [y for y in lst if x != y and y % x == 0] for x in set(lst)}
return sum(len(edges[y]) for x in lst for y in edges[x])
This should be significantly faster (1/7th the time in my test of lists with 100 elements).
The algorithm is essentially to build a directed graph where the nodes are the numbers in the list. Edges go from node A to node B iff the integer values of those nodes are different and A divides evenly into B.
Then traverse the graph. For each starting node A, find all the nodes B where there's an edge from A to B. On paper, we would then go to all the next nodes C, but we don't need to... we can just count how many edges are leaving node B and add that to our total.
EDIT
Depending on the distribution of values in the list, this is probably faster:
def foo(lst):
counts = Counter(lst)
edges = {x: [y for y in counts if x != y and y % x == 0] for x in counts}
return sum(counts[x] * counts[y] * sum(counts[z] for z in edges[y]) for x in counts for y in edges[x])
Here, you can think of nodes as having a numeric value and a count. This avoids duplicate nodes for duplicate values in the input. Then we basically do the same thing but multiply by the appropriate count at each step.
EDIT 2
def foo(lst):
counts = collections.Counter(lst)
edges = collections.defaultdict(list)
for x, y in itertools.combinations(sorted(counts), 2):
if y % x == 0:
edges[x].append(y)
return sum(counts[x] * counts[y] * sum(counts[z] for z in edges[y]) for x in counts for y in edges[x])
Slight improvement thanks to #Blckknght. Sorting the unique values first saves some time in enumeration.
EDIT 3
See comments, but the original code in this question was actually wrong. Here's code that (I think) does the right thing based on the problem description which can be found in the comments:
def foo3(lst):
count = 0
for x, y, z in itertools.combinations(lst, 3):
if y % x == 0 and z % y == 0:
count += 1
return count
print(foo3([1, 2, 3, 4, 5, 6])) # 3
print(foo3([6, 5, 4, 3, 2, 1])) # 0
EDIT 4
Much faster version of the previous code:
def foo4(lst):
edges = [[] for _ in range(len(lst))]
for i, j in itertools.combinations(range(len(lst)), 2):
if lst[j] % lst[i] == 0:
edges[i].append(j)
return sum(len(edges[j]) for i in range(len(lst)) for j in edges[i])
EDIT 5
More compact version (seems to run in about the same amount of time):
def foo5(lst):
edges = [[j for j in range(i + 1, len(lst)) if lst[j] % lst[i] == 0] for i in range(len(lst))]
return sum(len(edges[j]) for i in range(len(lst)) for j in edges[i])
Here's how I'd solve your problem. It should use O(N**2) time.
def count_triple_multiples(lst):
count = collections.Counter(lst)
double_count = collections.defaultdict(int)
for x, y in itertools.combinations(sorted(count), 2):
if y % x == 0:
double_count[y] += count[x] * count[y]
triple_count = 0
for x, y in itertools.combinations(sorted(double_count), 2):
if y % x == 0:
triple_count += double_count[x] * count[y]
return triple_count
My algorithm is very similar to the one smarx is using in his answer, but I keep a count of the number of edges incident to a given value rather than a list.
As far as speeding things up goes (instead of going recursive), testing with 1000 entries, slicing the sorted list at each level cut time by more than half for me (gets rid of numbers less than y, z at their respective levels:
def foo(L):
assert 0 not in L
L=sorted(L)
A = 0
for i,x in enumerate(L):
for j,y in enumerate(L[i + 1:]):
if x != y and not y % x:
for k,z in enumerate(L[i + j + 2:]):
if y != z and not z % y:
A = A + 1
return A

Merge Sort Index Error

def merge (seq, p, q, r):
# n1: length of sub-array [p..q]
n1 = q - p + 1
# n2: length of sub-array [q+1 ..r]
n2 = r - q
# Left and Right arrays
left_arr = []
right_arr = []
for i in xrange(0, n1):
left_arr.append( seq[p+i] )
for j in xrange(0, n2):
right_arr.append( seq[q+j+1] )
j=0
i=0
for k in xrange(p,r+1):
if left_arr[i]<= right_arr[j]:
seq[k]=left_arr[i]
i+=1
else:
seq[k]=right_arr[j]
j+=1
return seq
s = [2,4,5,7,1,2,3,6]
p = 0
q = 3
r = 7
print merge(s,p,q,r)
How it works:
A unsorted sequence s is taken, along with the index numbers where the sequence has to be merged. (p=initial, r = final, q=middle)
Now, left_arr and right_arr are [p,q], [q+1, r] respectively
we take left_arr and right_arr initial values (i=0, j=0). We iterate over the sequence seq...
while iterating we are replacing the values of seq with the sorted values...
The above function is able to sort till last number "7".. at the end, its showing "IndexError". I can use exception handling to catch it and fix but I think... for merge sort, its too much.. Can anyone help me in fixing the code as easy as possible.
I am learning Algorithms.. (following book: Introduction to Algorithms by Thomas H. Cormen)
the problem is that at the last iteration i will be equal to 4 and you are trying to access left_arr[5] which does not exist. you should add a stopping condition when i or j become larger then the size of the corresponding array, and then add all the remaining elements in the other array to seq.
Here is a code that works:
def merge (seq, p, q, r):
# n1: length of sub-array [p..q]
n1 = q - p + 1
# n2: length of sub-array [q+1 ..r]
n2 = r - q
# Left and Right arrays
left_arr = seq[p:n1] #here
right_arr = seq[n1:r+1] #here
j=0
i=0
for k in xrange(p, r+1):
if left_arr[i]<= right_arr[j]:
seq[k]=left_arr[i]
i+=1
if i > n1-1: #here
break
else:
seq[k]=right_arr[j] #here
j+=1
if j > n2-1:
break
if i >= len(left_arr): #from here down
seq[k+1:] = right_arr[j:]
elif j >= len(right_arr):
seq[k+1:] = left_arr[i:]
return seq
s = [2,4,5,7,1,1,1,1]
p = 0
q = 3
r = 7
print merge(s,p,q,r)
I have marked with comments the places where I have edited your code.
The problem with your code is that you're looping over xrange(p, r+1), so during that loop in some iteration the value of either i or j might become equal the value of len(left) or len(right), causing the IndexError eventually.
def merge(seq,p,q,r):
left=seq[p:q+1] #a better way to fetch the list
right=seq[q+1:]
i=0
j=0
#you shuldn't loop over the length of seq as that might make the value of either i or j
# greater than the length of left or right lists respectively.
# so you should only loop until one of the lists is fully exhausted
while i<len(left) and j<len(right):
if left[i] <= right[j] :
seq[i+j]=left[i]
i+=1
else:
seq[i+j]=right[j]
j+=1
#now the while loop is over so either right or left is fully traversed, which can be
#find out my matching the value of j and i with lenghts of right and left lists respectively
if j == len(right):
seq[i+j:]=left[i:] #if right is fully traversed then paste the remaining left list into seq
elif i==len(left): #this is just vice-versa of the above step
seq[i+j:]=right[j:]
print seq
s = [2,4,5,7,1,2,3,6]
p = 0
q = 3
r = 7
merge(s,p,q,r)
output:
[1, 2, 2, 3, 4, 5, 6, 7]
You didn't check the array index while iterating left_arr and right_arr.
You should merge the left part of either array when another array ends.
for k in xrange(p,r+1):
if left_arr[i]<= right_arr[j]:
seq[k]=left_arr[i]
i+=1
else:
seq[k]=right_arr[j]
j+=1
# --------------- add this ----------------
# if any array ends, merge the left elements of the other array
if i >= len(left_arr):
seq[k:] = right_arr[j:]
break
elif j >= len(right_arr):
seq[k:] = left_arr[i:]
break
# --------------- end ----------------
return seq

Categories