Compare many values and tell if neither of them equal - python

I'm working on a project and I need to compare some values between each other and tell if they DO NOT match. I have a list of thirteen lists and each of those have more than 500 values. All thirteen lists have the same length. I would like to find an index of the item in any of those thirteen lists.
However I tried to simplify the problem by making three lists and each of those contain four items.
list1 = [1, 2, 2, 2]
list2 = [1, 3, 2, 2]
list3 = [2, 4, 2, 2]
Blist = [list1, list2, list3]
for i in range(len(Blist)): #0, 1, 2
for j in range(len(Blist)): #0, 1, 2
if i == j:
pass
else:
for k in range(len(list1)): #0, 1, 2, 3
st = Blist[i][k] != Blist[j][k]
print(st)
I could compare two lists at a time but I can't come up with the solution that would compare all items with the same index and return me a value of the index "ind" whose values don't match (when you compare list1[ind], list2[ind] and list3[ind]).
If there were only three lists I could write
for i in range(len(list1)):
if (list1[i] != list2[i] and list1[i] != list3[i] and list2[i] != list3[i])
print(i)
But I'd like to solve a problem even if it has hundreds of lists with hundreds of items.

For every index, create a set of values taking values from a single index for each nested list. As a set can't have duplicate elements, the length of the set should be equal to the total number of nested lists. Otherwise, there were duplicates, which means all the values of that index were not unique.
values = [
[1, 2, 2, 2, 5],
[1, 3, 2, 2, 7],
[2, 4, 2, 2, 1]
]
n = len(values[0]) # Number of values in each nested list
total = len(values) # Total number of nested lists
for i in range(n):
s = {v[i] for v in values}
if len(s) == total:
print(i)
Output:
1
4
If you've understood the above approach, the code can be cut down using a somewhat functional approach. Basically 2 lines of python code. (written in multiple lines for improved readability).
values = [
[1, 2, 2, 2, 5],
[1, 3, 2, 2, 7],
[2, 4, 2, 2, 1]
]
total = len(values)
# Using a list comprehension to create a list with the unique indices
unique_indices = [
idx
for idx, s in enumerate(map(set, zip(*values)))
if len(s) == total
]
print(unique_indices)
Output:
[1, 4]

if you are allowed to use numpy,
array_nd = np.array(Blist)
uniqueValues , indicesList, occurCount= numpy.unique(array_nd, return_index=True, return_counts=True)
from the above filter all of them which has occurCount as 1 and you can get its index from indicesList.

Related

improve efficiency of a nested loop

I need to find the number of pairs with consecutive numbers in a list. If elements in the list are repeated, they should be treated as members of a distinct pair. For instance, if the list were [1, 1, 1, 2, 2, 5, 8, 8], then there are three ways to choose 1 and two ways to choose 2, or a total of 3×2=63×2=6 ways to choose the pair (1, 2), so that the answer would, in this case, be 6.
My solution currently contains a nested loop as below. The code works but I want to optimize for a runtime of less than 2 seconds.
Can anyone give me some pointers on how to improve the runtime of this solution?
L = [1, 2, 5, 8]
count = 0
for i in range(0,len(L)-1):
for x in range(i+1, len(L)):
if L[x] == L[i] + 1 or L[x] == L[i] -1 :
count+=1
You could use the Counter class from collection to classify and count the available numbers, then sum up the product of counts for existing pairs of consecutive values:
from collections import Counter
L = [1, 1, 1, 2, 2, 5, 8, 8]
counts = Counter(L)
r = sum(c*counts[n+1] for n,c in counts.items())
print(r) # 6

Fill lists in list with zeros if their length less than

I have a list of lists with different sizes but I want to make them all the same length. For example, make them with length of 5 by padding with zeros if length less than 5 or cut the list if length is more than 5. For example, I have a list:
foo = [
[1, 2, 3],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5, 6, 7]]
result = [
[1, 2, 3, 0, 0],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5]]
Do you have an idea of optimal and fast solution, if the list of lists is large?
List comprehension
Make a fill list and use slicing to get the appropriate lengths.
n = 5
fill = [0] * n
result = [sublist[:n] + fill[len(sublist):] for sublist in foo]
result = []
for sublist in foo:
size = len(sublist)
result.append(sublist[:5] + [0]*(5 - size))
To perform this optimization, i sliced additional elements beyond n = 5, and replaced with 0 those not reaching n = 5 by checking how many elements they miss.
def listOptimization(foo, n):
# slicing foo to have n elements on all child elements of foo
for i in range(len(foo)):
foo[i] = foo[i][:n]
# optimizing
for i in range(len(foo)):
# check if foo child element is less than n
# if true, append to the list with 0 depending on how many
# elements to reach n
if len(foo[i])<n:
temp = n-len(foo[i])
for x in range(temp):
foo[i].append(0)
return foo
result = [[bar[i] if i<len(bar)else 0 for i in range(5)] for bar in foo]
there are 5 elements in a row, so for i in range(5), the exceed 5 will be discard. then assign value directly,if the length of each row is less than i, assign 0
Actually, I found a pretty fast solution for me. If you have an idea how to solve it without a for loop please post.
for row in foo:
while len(row) < 5:
row.append(0)
else:
row[:5]

How to partition a list based on (sublist) indices of another list in Python

I have two lists, one containing some unique elements (integers in my case) and the other containing indices that indicate into which sublist of a newly created nested list the elements should be inserted.
elements = [1, 2, 3, 4, 5, 6]
indices = [0, 0, 1, 2, 2, 1]
expected_result = [[1, 2], [3, 6], [4, 5]]
The list of elements contains only unique items, potentially not sorted.
The list of indices is 'normalized' such that the lower indices will always occur first.
The new nested list should use the indices to determine the sublist of the expected result to which the elements shall belong.
I have come up with the following function, but I have a feeling that there should be an easier way.
def indices_to_nested_lists(indices: Sequence[int], elements: Sequence):
result = []
for i in range(max(indices)+1):
sublist = []
for j in range(len(elements)):
if indices[j] == i:
sublist.append(elements[j])
result.append(sublist)
return result
Can anyone think of an easier, maybe more pythonic way of achieving the same result?
Try using this for loop with zip:
l = [[] for i in range(max(indices) + 1)]
for x, y in zip(elements, indices):
l[y].append(x)
print(l)
Output:
[[1, 2], [3, 6], [4, 5]]

Get common elements majority of lists in python

Given 4 lists, I want to get elements that are common to 3 or more lists.
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5]
c = [1, 3, 4, 5, 6]
d = [1, 2, 6, 7]
Hence, the output should be [1, 2, 3, 4].
My current code is as follows.
result1 = set(a) & set(b) & set(c)
result2 = set(b) & set(c) & set(d)
result3 = set(c) & set(d) & set(a)
result4 = set(d) & set(a) & set(b)
final_result = list(result1)+list(result2)+list(result3)+list(result4)
print(set(final_result))
It works fine, and give the desired output. However, I am interested in knowing if there is an easy way of doing this in Python, ie: are there any built in functions for this?
Using a Counter, you can do this like:
Code:
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5]
c = [1, 3, 4, 5, 6]
d = [1, 2, 6, 7]
from collections import Counter
counts = Counter(sum(([list(set(i)) for i in (a, b, c, d)]), []))
print(counts)
more_than_three = [i for i, c in counts.items() if c >= 3]
print(more_than_three)
Results:
Counter({1: 4, 2: 3, 3: 3, 4: 3, 5: 2, 6: 2, 7: 1})
[1, 2, 3, 4]
Iterate over the values in all lists to create a dict of {value: number_of_lists_the_value_appears_in}:
from collections import defaultdict
counts = defaultdict(int)
for list_ in (a, b, c, d):
for value in set(list_): # eliminate duplicate values with `set`
counts[value] += 1
Then in the second step remove all values with a count < 3:
result = [value for value, count in counts.items() if count >= 3]
print(result) # [1, 2, 3, 4]
The code below will solve the generalised problem (with n lists, and a requirement that a common element must be in at least k of them). It will work with non-hashable items, which is the main disadvantage of all the other answers:
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5]
c = [1, 2, 3, 4, 4, 5, 6]
d = [1, 2, 6, 7]
lists = [a, b, c, d]
result = []
desired_quanity = 3
for i in range(len(lists) - desired_quanity + 1): #see point 1 below
sublist = lists.pop(0) #see point 2
for item in sublist:
counter = 1 #1 not 0, by virute of the fact it is in sublist
for comparisonlist in lists:
if item in comparisonlist:
counter += 1
comparisonlist.remove(item) #see point 3
if counter >= desired_quanity:
result.append(item)
This has the disadvantage that for each element in every list, we have to check in every other list to see if it is there, but we can make things more efficient in a few ways. Also look-ups are alot slower in lists than sets (which we can't use since the OP has non-hashable items in the lists), and so this may be slow for very large lists.
1) If we require an item to be in k lists, we don't need to check each item in the last k-1 lists, as we would have already picked it up whilst searching through the first k lists.
2) Once we have searched through a list, we can discard that list, since any items in the just-searched-list that might contribute to our final result, will again already have been dealt with. This means that with each iteration we have fewer lists to search through.
3) When we have checked if an item is in enough lists, we can remove that item from the list, which means not only is the number of lists getting shorter as we proceed, the lists themselves are getting shorter, meaning quicker lookups.
As an aftersort, if we the original lists happen to be sorted beforehand, this might also help this algorithm work efficiently.
create a dictionary of counts and filter out those with count less than 3

Creating a new list when before it reaches a number

How do I create a new list that contains sublists of ints but the way of divide it is when the next number is the minimun (or equal to the first value founded)?
For example
List1=[1,2,3,4,5,1,2,3,4,1,2,3,4,5,6]
The output that I am looking for is shown below:
Complete_List=[[1,2,3,4,5],[1,2,3,4],[1,2,3,4,5,6]]
I tried looping through the list and appending it when the value is greater than 1 . However it will not work as it doesn't create another list inside it.
Do I have to right a regex for this problem?
Some guidance would be really helpful.
Thank you
Here's something that will split a generic iterable on a given value.
def split_on_value(iterable, split_value):
iterator = iter(iterable)
outer, inner = [], [next(iterator)]
for value in iterator:
if value == split_value:
outer.append(inner)
inner = []
inner.append(value)
outer.append(inner)
return outer
value_list = [1, 2, 3, 4, 5, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
print split_on_value(value_list, 1)
# [[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
print split_on_value(value_list, 3)
# [[1, 2], [3, 4, 5, 1, 2], [3, 4, 1, 2], [3, 4, 5, 6]]
A vanilla, straightforward, CS101 solution. Though, possibly the most efficient one, because it scans the list exactly once. It also does not assume that segments begin with 1.
fragment = []
result = []
prev = List1[0] - 1 # Preset the previous element marker
for n in List1:
if n > prev:
fragment.append(n)
else:
result.append(fragment)
fragment = [n]
prev = n
result.append(fragment)
#[[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
First you search for the 1's, or whatever your condition is, and get the indices within the list. Don't forget to append the len(list) to include the last segment.
idx = [i for i, l in enumerate(List1) if l == 1] + [len(List1)]
Optional, if you want the beginning end of the List. That is, you do not know if there will be a 1 always at index 0.
idx = [0] + idx if idx[0] != 0 else idx
Then, split the list at those indices you found.
complete_list = [List1[ind1:ind2] for ind1, ind2 in zip(idx[:-1], idx[1:])]
and the result:
[[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
You can try this to split at every instance of 1:
List1=[1,2,3,4,5,1,2,3,4,1,2,3,4,5,6]
print [map(int, list("1"+i)) for i in ''.join(map(str, List1)).split("1")][1:]
By mapping over List1 with the string function, we can then join all the numbers in the list into one large string. From there, the algorithm splits itself at each instance of one, creating a list containing the new strings of digits. from there, the code maps the integer function over a list created of the strings and appending 1 at the front of the string to make up for the lost 1 when it originally split, creating a list within a list.

Categories

Resources