Intersection in Python keeping order [closed] - python

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I want to find intersection of two lists with original order. So if
a = [2, 3, 5, 6]
b = [17, 28, 2, 8, 0, 3]
I need [2, 3]. But for [3, 2, 5, 6] and [17, 28, 2, 8, 0, 3] I need [2].
How I can get it?

def inter(a,b):
c = []
for num in a:
if num in b and (not len(c) or b.index(c[-1]) < b.index(num)):
c.append(num)
return c

You will have to loop over one list, searching the element in the other list, and only search the other list from that point for any following element.
Code could be:
a = [3, 2, 5, 6]
b = [17, 28, 2, 8, 0, 3]
def intersect(a,b):
result = []
ix = 0
for x in a:
try:
ix = b[ix:].index(x)
result.append(x)
except ValueError:
pass
return result
print(intersect(a, b))
It gives:
[3]
NB: if you want [2], just use intersect(b, a)

This is more of a research problem than a StackOverflow question...
The phrase to search for is "longest common subsequence problem". Once you've found an algorithm that suits your situation, it will probably be clearer how to translate it into Python, or you can ask more targeted questions here.

a = [7, 8, 6, 6, 8, 10, 3, 3]
b = [7, 4, 10, 8, 7]
def magic(a, b):
result = []
# early abort
if len(a) == 0 or len(b) == 0:
return result
try:
index_b = b.index(a[0])
result.append(b[index_b])
# a[0] was found in b at index index_b. Disregard any items before index_b in the future.
# And since we found a[0], we can remove it from our search list a.
# continue with next search
return result + magic(a[1:], b[index_b+1:])
except ValueError:
# a[0] is not in b. continue with next item in a.
# No need for new code, just do the same function again for a shorter a.
return result + magic(a[1:], b)
print(magic(a,b))
This prints [7,8]. I hope the code is self-explanatory.
It fulfills your test case 1:
a = [2, 3, 5, 6]
b = [17, 28, 2, 8, 0, 3]
# prints [2, 3]
It does not fulfill your test case 2:
>>> a = [3, 2, 5, 6]
>>> b = [17, 28, 2, 8, 0, 3]
>>> tmp.magic(a,b)
[3]
# you wanted [2]
But it does follow my earlier comment (to which you said "YES")
#jevik So what you want to do is to take a[0] iff it is in b, then take a[1] iff it is in b after the index where a[0] was in b and so on? – lucidbrot 41 mins ago
Your test case 3 from a comment:
For a=7 8 6 6 8 10 3 3 and b=7 4 10 8 7 with your solve I get 7 8 10. I need to get 7 8
My program does print [7,8] for that.

You can use & operation between two sets, that will give you an intersection set without change in order
a = [2, 3, 5, 6]
b = [17, 28, 2, 8, 0, 3]
intersection = list(set(a) & set(b))
print(intersection)

Related

Finding changes made to lists [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 11 months ago.
Improve this question
So I'm trying to find the items that went in and went out of a list before and after it changed
For example, at first my list is:
[1, 1, 2, 5, 7, 7]
And then a minute later it's:
[1, 2, 2, 5, 6, 7, 4]
How would I end up with 2 lists that show what items went out and which went in like so,
itemsOut = [1,7]
itemsIn = [2, 6, 4]
Position and length can change
You can use Counter from the built-in collections module:
>>> list1 = [1, 1, 2, 5, 7, 7]
>>> list2 = [1, 2, 2, 5, 6, 7, 4]
>>> from collections import Counter
>>> counter1 = Counter(list1)
>>> counter2 = Counter(list2)
>>> l_diff = counter1-counter2
>>> r_diff = counter2-counter1
>>> print(list(l_diff))
[1, 7]
>>> print(list(r_diff))
[2, 6, 4]
You could use something like this, to detect wich items changed.
old_arr = [1, 2, 2, 5, 7, 7]
new_arr = [1, 1, 2, 5, 7, 7]
items_out = []
for element in old_arr:
if new_arr.count(element) > 0:
new_arr.pop(new_arr.index(element))
else:
items_out.append(element)
print("Items out:", items_out)
print("Items in:", new_arr)

Constructive Insertion Sort

I having trouble with insertion sort and I feel I might be missing the point of the sort and misunderstanding the fundamentals.
We were given a insertion sort which edited the array which was fed into it. We are tasked with then modifying the code we have been given to then create a constructive insertion sort which will not edit the original array
This is the code I have so far
def append(A, x):
return A + [x]
def insertionSort(A):
B = []
B = append(B, A[0])
for i in range(1, len(A)):
B = insert(B, A[i], i, A)
return str(B)
def insert(B, k, hi, A):
print(B, k, hi)
for x in range(hi-1, -1, -1):
if B[x] <= k:
B = append(B, k)
return B
B = append(B, A[x])
B[0] = k
return B
print(insertionSort([2,4,6,8,10,1,3,5,7,9]))
However after the third or forth element in the list it begins adding all the items to the end of the list in reverse order
[2] 4 1
[2, 4] 6 2
[2, 4, 6] 8 3
[2, 4, 6, 8] 10 4
[2, 4, 6, 8, 10] 1 5
[1, 4, 6, 8, 10, 10, 8, 6, 4, 2] 3 6
[1, 4, 6, 8, 10, 10, 8, 6, 4, 2, 1, 10, 8, 6, 4, 3] 5 7
[1, 4, 6, 8, 10, 10, 8, 6, 4, 2, 1, 10, 8, 6, 4, 3, 3, 1, 10, 8, 6, 5] 7 8
[1, 4, 6, 8, 10, 10, 8, 6, 4, 2, 1, 10, 8, 6, 4, 3, 3, 1, 10, 8, 6, 5, 7] 9 9
[1, 4, 6, 8, 10, 10, 8, 6, 4, 2, 1, 10, 8, 6, 4, 3, 3, 1, 10, 8, 6, 5, 7, 9]
I cannot wrap my head around why this is wrong.
Thanks dearly to anyone who can help.
The reverse problem is at the foor loop in the insert function
when your loop hit those values it starts the reverse mode
def insert(B, k, hi, A):
# when hi=5
for x in range(hi-1, -1, -1):
# x = 4
# here B[4] is 10 and k=1 so B[4] <= 1 is False
# you program does not execute the inside of if
# instead it jumps to B = append(B, A[x]) where A[4] == 10
# and the this loop goes in reverse mode from 4 to 0
# when x = 3
# B[x] = 8 so 8 is not less or equal of k where k = 1
# so it jumps again to B = append(B, A[x]) where A[x] = A[3] = 8
# so it append 8
# and so on
# when this loop is finished your list will look like [1,4,6,8,10,10,8,6,4,2]
# the 1 gets added when the loop is finished at B[0] = k
# and then rest of the outputs are result of the loop inside the insertionSort func
if B[x] <= k:
B = append(B, k)
return B
B = append(B, A[x])
B[0] = k
return B
Here is a solution:
def insertionSort(A):
copy_sort = A.copy()
for i in range(1, len(copy_sort)):
item = copy_sort[i]
j = i - 1
while j >= 0 and copy_sort[j] > item:
copy_sort[j + 1] = copy_sort[j]
j -= 1
copy_sort[j + 1] = item
return copy_sort
your_array = [2,4,6,8,10,1,3,5,7,9]
sorted = insertionSort(your_array)
print(your_array)
print(sorted)
You need to work out your algorithm on paper, and then translate those steps to Python code. What you've implemented is convoluted and incorrect.
Most of all, insert is very confused as to the information it needs and how it should do its job. As best I can see from your code, you want this routine to insert a given value k into the appropriate location in list B. For some reason, you've also passed in list A and the value's location in that list, neither of which is applicable.
What your routine does then is strange; starting from the end of B (using i instead of B itself), the code checks the elements of B; every time it finds a value in the list less than the new one, it appends the new one to the end of B. Regardless of that comparison, it appends the corresponding element of A to B.
Nowhere do you insert the element in the proper place.
Rewrite this code. Start with the minimum necessary information:
def insert(arr, new_val):
# insert new_val into the list arr
Now, your function has two steps to carry out:
Find the proper position for new_val
Make a new list with the value inserted into that spot.
You return that new list.
Can you move on from there?

Creating a List of like Indicies [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
new here and somewhat new to Python. I am having a blank and would appreciate some guidance.
I am trying to write a nested list, then write a for loop to print a comma seperated list of the numbers using like indicies from each list to pair-up the numbers. I have my code below(2.7.14):
FirstNumbers = [1, 2, 3]
SecondNumbers = [4, 5, 6]
ThirdNumbers = [7, 8, 9]
NestedNumbers = [FirstNumbers, SecondNumbers, ThirdNumbers]
for i in range(0, 3):
for each_number in NestedNumbers:
print each_number[i],
#Output
1 4 7 2 5 8 3 6 9
My current issue is trying to get the numbers to read [1, 4, 7] and so on. I would appreciate any guidance.
Thank you
I think you need empty list so you can append the numbers to that i.e
FirstNumbers = [1, 2, 3]
SecondNumbers = [4, 5, 6]
ThirdNumbers = [7, 8, 9]
NestedNumbers = [FirstNumbers, SecondNumbers, ThirdNumbers]
m = []
for i in range(0, 3):
k = []
for each_number in NestedNumbers:
k.append(each_number[i])
m.append(k)
Output m : [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
You need to put "print "[" after "for i in range(0,3)" and a print "]" after the "for each_number" part
so that it will print:
[1 4 7][2 5 8][3 6 9]
Now you need to insert a print ", " before "print each_number[i]"
You will end up with:
[, 1, 4, 7][, 2, 5, 8][, 3, 6, 9] You need to not use print ", " on the first run-through as that is printing the comma before the first number.
You will need to change the second for statement to not go through "NestedNumbers" because you cannot know if it is the first one, but instead "range(0,len(NestedNumbers))" so that you can check to see if it is the first iteration and whether you need to add a comma.
change it to:
FirstNumbers = [1, 2, 3]
SecondNumbers = [4, 5, 6]
ThirdNumbers = [7, 8, 9]
NestedNumbers = [FirstNumbers, SecondNumbers, ThirdNumbers]
for x in range(0,3):
print "["
for y in range(0,len(NestedNumbers)):
if y!=0: print ", "
print NestedNumbers[y][i]
print "]"
Now it will print:
[1, 4, 7][2, 5, 8][3, 6, 9]
Try this
import itertools
for n in itertools.chain(*zip(FirstNumbers,SecondNumbers,ThirdNumbers))
print n,
The call to zip combines the lists as you want as a list of tuples. Then itertools.chain flattens this to a single list.
if you actually want this output:
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
Just print the result of calling zip

random generation of list elements with rules

Let list l consists of n elements, where
each element should be either 0 or a positive integer less than or equal to r, and
the sum of list should be equal to m
Example:
Given n = 5, r = 4, m = 10
l = [4, 3, 2, 0, 1]
It is easy to fulfill rule(1), but I wonder if there is any good idea/algo to fulfill both rules?
Here's a simple brute force solution. Basically, you want to generate samples where random integers less than r are equally likely. Samples that do not meet the criteria (sum to m) are rejected.
import numpy as np
def rand_with_rules(n, r, m):
if n*(r-1) < m:
raise ValueError
while True:
l = np.random.randint(0, r, size=(n))
if l.sum() == m:
return l
Note that the rejection of samples will necessarily bias your "random" numbers. Because of your constraints, you can't have a purely set of random and certain will tend to be over or underrepresented.
Take, for example, the case n, r, m = 2, 3, 4. The only series that matches this criteria is (2, 2), so the likelihood of drawing 2 is 100% and 0% for other values.
In essence, this solution states that you have no prior knowledge of which integers are most likely. However, through the constraints, your posterior knowledge of the numbers will almost never be truly uniform.
There's probably a clever analytic solution for the distribution of integers given this constraint, which you could use to generate samples. But I don't know what it is!
Without validation that all rules can be meet at once, I suggest following solution
import random
def dis(n,m,r):
l = [0] * n
for inc in range(1,m):
l.sort()
for ind_less_r, less_r in enumerate(l):
if less_r > r:
break
l[random.randint(0,ind_less_r-1)] += 1
random.shuffle(l)
return l
for i in range(1,10):
print dis(10,50,9)
result is
[7, 7, 6, 1, 6, 3, 5, 4, 4, 6]
[5, 6, 7, 5, 4, 7, 4, 4, 4, 3]
[4, 3, 2, 4, 7, 7, 5, 7, 5, 5]
[4, 4, 5, 6, 4, 6, 6, 4, 5, 5]
[6, 6, 4, 6, 5, 6, 2, 5, 4, 5]
[2, 8, 4, 2, 6, 5, 4, 4, 6, 8]
[6, 6, 3, 4, 5, 5, 5, 5, 6, 4]
[6, 4, 5, 6, 7, 3, 1, 5, 6, 6]
[4, 5, 4, 7, 6, 6, 3, 2, 6, 6]
A reasonable interpretation would be to (i) find and count all unique lists satisfying the rules; and (ii) pick one of those lists at random with equal probability. The problem with this method is that it is complicated to code the list of lists.
A simpler way to do it (less code) is to expose each correct list to the same probability of being picked. That algorithm is:
check that m<=nr If not, return None or raise an error.
Loop: repeatedly generate lists with random numbers in [0,r]. Break the loop and return the first list of sum m.
Note: The tradeoff here for less code is potentially longer execution time. if r is large, or m is an improbable sum, this may take a while. We can mitigate this a little by checking the limits where the answer can only be zeros or r-1's.
from numpy.random import randint
def known_sum_random_list(size,limitint,knownsum):
if knownsum>size*(limitint-1):
return None
if knownsum==size*(limitint-1):
return size*[limitint-1]
s=0
l = size*[0]
while (s!=knownsum):
l = randint(0,limitint,size)
s = sum(l)
return l
for t in xrange(10):
print known_sum_random_list(5,4,10)
output:
[3 2 1 2 2]
[1 1 2 3 3]
[3 0 3 1 3]
[3 2 0 3 2]
[2 2 0 3 3]
[1 3 2 3 1]
[3 3 0 3 1]
[2 0 2 3 3]
[3 1 2 3 1]
[3 2 0 3 2]
Since you responded in comments that it can have many 0s and numbers can repeat, I infer that it need not be all that random. With that in mind, here is a basic solution without any loops or includes. It assumes n, r, and m have valid values and types. But such checks are simple enough to add, I'll edit them in upon request.
def create_list(n, r, m):
output = [r] * (m/r)
remainder = m - r * len(output)
if remainder != 0:
output += [m - r * len(output)]
output += [0] * (n - len(output))
return output
output = create_list(5, 4, 10) # gives [4, 4, 2, 0, 0]
output = create_list(5, 2, 10) # gives [2, 2, 2, 2, 2]
P.S. - request was for values less than r, but example showed values equaling r, so this is going by the example

how to merge two sublists sharing any number in common?(2)

Given that:
list=[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11]]
I have asked a similar question before, I have tried the code on
how to merge two sublists sharing any number in common?
but I am stuck in my code now.
I want to merge the sublists that share a common number,
e.g. [1,2,3] and [3,4,5] can merge to give [1,2,3,4,5] as they share a common number, 3.
In [[1,2,3],[3,4,5],[5,6]], although [1,2,3] and [3,4,5] share a common number, 3,
[3,4,5] and [5,6] also share a common number, 5, so I want all three of them to merge then gives
[1,2,3,4,5,6].
So for list,
my expected result is:
[[1,2,3,4,5,6,7],[9,10,11]]
I have tried the following code but don't know what is wrong, can anyone help?
s = map(set, list)
for i, si in enumerate(s):
for j, sj in enumerate(s):
if i != j and si & sj:
s[i] = si | sj
s[j] = set()
list=[list(el) for el in s if el]
print list
>>>[[5, 6, 7], [9, 10, 11]]
def merge_containing(input_list):
merged_list = [set(input_list[0])]
i = 0
for sub_list in input_list:
if not set(sub_list).intersection(set(merged_list[i])): # 1
merged_list.append(set()) # 2
i += 1 # 2
merged_list[i].update(set(sub_list)) # 3
return [sorted(sub_list) for sub_list in merged_list] # 4
mylist=[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11]]
print merge_containing(mylist)
Output:
[[1, 2, 3, 4, 5, 6, 7], [9, 10, 11]]
How does it work:
Check if the sub_list set shares any common member with the current
index set of the merged_list.
If it doesn't, add a new empty set to the merged_list and increment
the index.
Adds the sub_list set to the set at index in the merged_list.
Converts from set to list and return
def merge(list_of_lists):
number_set = set()
for l in list_of_lists:
for item in l:
number_set.add(item)
return sorted(number_set)
if __name__ == '__main__':
list_of_lists = [[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11]]
merged = merge(list_of_lists)
print merged
I'm posting this as a new answer since the OP already accepted my other.
But as pointed out by #Eithos,
the input:
[[3,4], [1,2], [1,3]]
should return
[[1,2,3,4]]
and the input
[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11],[65,231,140], [13,14,51]]
should return
[[1, 2, 3, 4, 5, 6, 7], [9, 10, 11], [13, 14], [51], [65], [140], [231]]
Here's my attempt:
from itertools import chain
def merge_containing(input_list):
print " input:", input_list
chain_list = sorted(set(chain(*input_list))) # 1
print " chain:",chain_list
return_list = []
new_sub_list = []
for i, num in enumerate(chain_list):
try:
if chain_list[i + 1] == chain_list[i] + 1: # 2
new_sub_list.append(num)
else:
new_sub_list.append(num) # 3
return_list.append(new_sub_list)
new_sub_list = []
except IndexError:
new_sub_list.append(num) # 3
return_list.append(new_sub_list)
print 'result:', return_list
return return_list
mylist = [[3,4], [1,2], [1,3]]
merge_containing(mylist)
print
mylist = [[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11],[65,231,140], [13,14,51]]
merge_containing(mylist)
Output:
input: [[3, 4], [1, 2], [1, 3]]
chain: [1, 2, 3, 4]
result: [[1, 2, 3, 4]]
input: [[1, 2, 3], [3, 4, 5], [5, 6], [6, 7], [9, 10], [10, 11], [65, 231, 140], [13, 14, 51]]
chain: [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, 14, 51, 65, 140, 231]
result: [[1, 2, 3, 4, 5, 6, 7], [9, 10, 11], [13, 14], [51], [65], [140], [231]]
Explanation:
This one is a little hacky them the last one
I use itertool.chain to flat all the lists and them I sort it.
Then I check if the current number is within the range of 1 digit from the next
If it is I store it in the new_sub_list, if not I store in the new_sub_list, then store new_sub_list in the return_list, and empty the new_sub_list.
Note the try/except Index Error, it to avoid comparing the last item of the list with one that doesn't exist,
Well... I couldn't resist answering #f.rodrigues' last answer with one of my own.
I have to be honest though, this final version was heavily influenced by jonrsharpe's solution (the code went through various revisions, each one more efficient until I realized his method was the only way to press the most amount of juice) over here: Using sublists to create new lists where numbers don't repeat
Which made me wonder... why are we answering the same question over and over again (from the very same person)? This question, how to merge two sublists sharing any number in common? and the one with jonrsharpe's solution.
Anyway, this joins lists in the way outlined in his first question, but, like the solutions he already received over there, this one also works just as well for solving this problem.
sequence = [[1, 4, 9], [2, 3, 6], [4, 13, 50], [13, 23, 29], [2, 3, 7]]
def combineSequences(seq):
for index, y in enumerate(seq):
while True:
for x in seq[index + 1:]:
if any(i in x for i in seq[index]):
seq.remove(x)
y.extend(x)
break
else:
index += 1
break
return [sorted(set(l)) for l in seq]
sequence = [[1, 4, 9], [2, 3, 6], [4, 13, 50], [13, 23, 29], [2, 3, 7]]
print combineSequences(sequence)
>>> [[1, 4, 9, 13, 23, 29, 50], [2, 3, 6, 7]]
sequence = [[3, 4], [1, 2], [1, 3]]
print combineSequences(sequence)
>>> [[1, 2, 3, 4]]
This solution operates under a different assumption than the one I made earlier, just to clarify. This simply joins lists that have a common number. If the idea, however, was to only have them separated by intervals of 1, see my other answer.
That's it!
Okay. This solution may be difficult to grasp at first, but it's quite logical and succint, IMO.
The list comprehension basically has two layers. The outer layer will itself create separate lists
for each value of i (outer index) that satisfies the condition that the value it points to in the list is not equal to the value pointed to by the previous index + 1. So every numerical jump greater than one will create a new list within the outer list comprehension.
The math around the second (inner list comprehension) condition is a bit more complicated to explain, but essentially the condition seeks to make sure that the inner list only begins counting from the point where the outer index is at, stopping to where once again there is a numerical jump greater than one.
Assuming an even more complicated input:
listVar=[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11],[65,231,140], [13,14,51]]
# Flattens the lists into one sorted list with no duplicates.
l = sorted(set([x for b in listVar for x in b]))
lGen = xrange(len(l))
print [
[l[i2] for i2 in lGen if l[i2] + i == l[i] + i2]
for i in lGen if l[i] != l[i-1] + 1
]
>>> [[1, 2, 3, 4, 5, 6, 7], [9, 10, 11], [13, 14], [51], [65], [140], [231]]

Categories

Resources