Relationship between lists (Python) - python

I have two lists, I want to sort the first one so I get them in ascending order, but I need the second list elements to be sorted with the element of the other list which they originally corresponded and I have no clue how to do that. Example:
Original imput:
l1 = [13, 1, 31, 6, 42, 99]
l2 = [14, 5, 11 ,7, 15, 12]
l1.sort()
Expected Result:
l1 = [1, 6, 13, 31, 42, 99]
l2 = [5, 7, 14 ,11, 15, 12]

Pack them in a tuple and sort it, then unpack as needed:
t = tuple(zip(l1, l2))
l_sorted = sorted(t, key=lambda e: e[0])
l1 = [e[0] for e in l_sorted]
l2 = [e[1] for e in l_sorted]

You can use sorted.
def sort_list(list1, list2):
zipped_pairs = zip(list2, list1)
j=sorted(zipped_pairs)
z = [x for _, x in j]
m = [__ for __, y in j]
print(z,m)
l1 = [13, 1, 31, 6, 42, 99]
l2 = [14, 5, 11 ,7, 15, 12]
print(sort_list(l1, l2))

Its faster if you can use numpy arrays. For example
import numpy as np
arr1 = np.array([13, 1, 31, 6, 42, 99])
arr2 = np.array([14, 5, 11 ,7, 15, 12])
arr1_sorted = np.sort(arr1)
arr2_sorted = arr2[arr1.argsort()]
# The expected answer would be
# arr1_sorted = array([1, 6, 13, 31, 42, 99])
# arr2_sorted = array([5, 7, 14 ,11, 15, 12])
Here sort() returns the sorted array, while argsort() returns the index corresponding to a sorted array

Unlikely to be fastest solution, but beautiful to my eye :)
l1 = [13, 1, 31, 6, 42, 99]
l2 = [14, 5, 11 ,7, 15, 12]
def delegated_sort(l1, l2):
return map(list, zip(*sorted(zip(l1, l2))))
>>> delegated_sort(l1, l2)
[[1, 6, 13, 31, 42, 99], [5, 7, 14, 11, 15, 12]]`

Related

Splitting a list on non-sequential numbers

I have an ordered list of entities, numbered in a broken sequence:
[1, 2, 3, 6, 7, 11, 17, 18, 19]
I'd like to break the list where there's a gap, and collect the results in a new list:
[[1, 2, 3], [6, 7], [11], [17, 18, 19]]
I have the feeling there's a name for what I want to do and probably a nice library function for it - but I can't think of it. Can anyone shine some light before I possibly reinvent a wheel?
edit: Thanks, folks, but I was asking if there's a name for this operation and an existing algorithm, not for implementations - this is what I came up with:
def group_adjoining(elements, key=lambda x: x):
"""Returns list of lists of contiguous elements
:key: function to get key integer from list element
"""
if not elements:
return elements
result = [[elements[0]]]
for a, b in zip(elements, elements[1:]):
if key(a) + 1 == key(b):
result[-1].append(b)
else:
result.append([b])
return result
Plain itertools.groupby approach:
from itertools import groupby
lst = [1, 2, 3, 6, 7, 11, 17, 18, 19]
out = []
for _, g in groupby(enumerate(lst), lambda x: x[0] - x[1]):
out.append([v for _, v in g])
print(out)
Prints:
[[1, 2, 3], [6, 7], [11], [17, 18, 19]]
Try greedy approach:
lst = [1, 2, 3, 6, 7, 11, 17, 18, 19]
res = []
tmp = []
prv = lst[0]
for l in lst:
if l-prv > 1:
res.append(tmp)
tmp = []
tmp.append(l)
prv = l
res.append(tmp)
print(res)
Output: [[1, 2, 3], [6, 7], [11], [17, 18, 19]]
I first came across more_itertools today, and I think this package is useful for this problem.
pip install more-itertools
from more_itertools import split_when
l = [1, 2, 3, 6, 7, 11, 17, 18, 19]
res = list(split_when(l, lambda a, b: a + 1 != b))
print(res)
You could use a simple generator.
def split(lst):
result = []
for item in lst:
if (not result) or result[-1] + 1 == item:
result.append(item)
else:
yield result
result = [item]
if result:
yield result
foo = [1, 2, 3, 6, 7, 11, 17, 18, 19]
result = [i for i in split(foo)]
print(result) # [[1, 2, 3], [6, 7], [11], [17, 18, 19]]
This assumes a sorted homogeneous list of int.
You could always avoid the sorted assumption with for item in sorted(lst):.
It's pretty easy by using this simple function:
li = [1, 2, 3, 6, 7, 9, 10, 11, 12, 14, 16, 17, 18]
def split(li):
result = []
temp = [li[0]]
for i in range(1, len(li)):
if li[i] - temp[-1] == 1:
temp.append(li[i])
else:
result.append(temp)
temp = [li[i]]
result.append(temp)
return result
print(split(li))

How to get the deepest list in list with abstract element?

Here I have some lists with any particular element (a, b, and c)
a = [2, 4, [9, 10]]
b = [1, 3, 5, 9, [11, 13, 14, 15, [16, 17, 19, 24]]]
c = [2, 4, [5, 11, 13, [14, 17, 29, [31, 19]]]]
npA = np.array(a, dtype = object)
npB = np.array(b, dtype = object)
npC = np.array(c, dtype = object)
I am trying to get the deepest list in each list a, b, c
print(npA[-1]) # it shows [9,10]
print(npB[-1][-1]) # it shows [16, 17, 19, 24]
print(npC[-1][-1][-1]) # it shows [31, 19]
How do get the generalization of this problem? or is there any built-in function of NumPy that can directly handle this?
You can solve this recursively, without numpy:
from typing import List
def deepest_list(l: List) -> List:
last_item = l[-1]
if isinstance(last_item, list):
return deepest_list(last_item)
return l
output:
deepest_list(c)
[31, 19]

How to merge multiple lists by their indices

I want to combine a nested list (lists in it have different lengths) into one. I would like to add by index first. Then sort by size.
Example:
lsts = [
[7, 23, 5, 2],
[3, 8, 1],
[99, 23, 9, 23, 74]
]
merged = [3, 7, 99, 8, 23, 23, 1, 5, 9, 2, 23, 74]
I would like to solve this without importing.
Assuming your list-of-lists cannot contain Nones, you can do this with itertools.zip_longest:
from itertools import zip_longest
result = []
for row in zip_longest(*lsts):
row = (x for x in row if x is not None)
for x in sorted(row):
result.append(x)
print(result)
Here is a one-liner:
import functools
import itertools
functools.reduce(lambda x,y: x+y, [sorted(x for x in p if x is not None) for p in itertools.zip_longest(*lsts)])
Output:
[3, 7, 99, 8, 23, 23, 1, 5, 9, 2, 23, 74]
I'll explain the solution step-by-step with each building on the result of the previous step.
To group items from each list by their indexes, itertools.zip_longest() is the tool for that:
>>> import itertools as it
>>> MISSING = object() # a sentinel
>>> lsts = [
[7, 23, 5, 2],
[3, 8, 1],
[99, 23, 9, 23, 74]
]
>>> it.zip_longest(*lsts, fillvalue=MISSING)
>>> list(_)
[(7, 3, 99), (23, 8, 23), (5, 1, 9), (2, <object object at 0x7f529e9b4260>, 23), (<object object at 0x7f529e9b4260>, <object object at 0x7f529e9b4260>, 74)]
This groups list elements into n-tuples using the MISSING fill value where needed, because lists might not be of equal length.
The next step is to iterate over each n-tuple and sort it internally (while skipping the MISSING values). The built-in function sorted() comes handy here:
>>> list(
sorted(x for x in ntuple if x is not MISSING)
for ntuple in it.zip_longest(*lsts, fillvalue=MISSING)
)
[[3, 7, 99], [8, 23, 23], [1, 5, 9], [2, 23], [74]]
The final step is to flatten this sequence of lists, and we'll use itertools.chain,from_iterable():
>>> list(it.chain.from_iterable(
sorted(x for x in ntuple if x is not MISSING)
for ntuple in it.zip_longest(*lsts, fillvalue=MISSING)
))
[3, 7, 99, 8, 23, 23, 1, 5, 9, 2, 23, 74]
The good thing about chain.from_iterable() is that it doesn't repeatedly concatenate smaller lists into the longer and longer final list, making it efficient. It also does this at the C level, AFAIK.
It's worth noting that None can also be used instead of the MISSING sentinel, but I used MISSING to also demonstrate how fillvalue works (e.g. you might want to use a zero instead or something else, if you wish).
zip_longest makes the work. The rest is cleaning/formatting
In [1]: from itertools import zip_longest, chain
In [2]: lsts = [
...: [7, 23, 5, 2],
...: [3, 8, 1],
...: [99, 23, 9, 23, 74]
...: ]
In [3]: [v for v in chain.from_iterable(zip_longest(*lsts)) if v !=None]
Out[3]: [7, 3, 99, 23, 8, 23, 5, 1, 9, 2, 23, 74]

How can I shuffle a list with no repetition?

array1=[3, 7, 11, 12, 15, 5, 1, 4]
array2=[14, 10, 9, 16, 2, 13, 6, 8]
I shuffled these arrays.
shuffled1 = sorted(array1, key=lambda k: random.random())
shuffled2 = sorted(array2, key=lambda k: random.random())
However, I do not want the same numbers to come to the same indexes again. For example: 3 and 14 at index 0, 7 and 10 at index 1. I don't want these numbers to be reciprocal again. All numbers need to be couple with another number.
For example:
shuffled1 =[1, 15, 4, 12, 7, 5, 3, 11]
shuffled2 =[13, 8, 9, 14, 2, 16, 6, 10]
İf shuffled arrays not like this, shuffle again.
As first step you can create a dictionary that will be used if shuffled lists array1 and array2 don't contain the values from initial state. Then we repeat shuffling until the condition is met.
For example:
from random import random
array1=[3, 7, 11, 12, 15, 5, 1, 4]
array2=[14, 10, 9, 16, 2, 13, 6, 8]
disallowed_dict = {}
for a, b in zip(array1, array2):
disallowed_dict.setdefault(a, []).append(b)
while any(b in disallowed_dict[a] for a, b in zip(array1, array2)):
array1 = sorted(array1, key=lambda _: random())
array2 = sorted(array2, key=lambda _: random())
print(array1)
print(array2)
Prints (for example):
[1, 15, 4, 7, 5, 11, 12, 3]
[16, 6, 10, 13, 8, 14, 9, 2]
I had completely missread the condition. Try this:
import numpy as np
from random import randint
array1=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def my_shuffle(array1, array2):
narray1 = np.array(array1)
idxs = []
for element in array1:
idx = randint(0,len(array1)-1)
while idx in idxs:
idx = randint(0,len(array1)-1)
idxs.append(idx)
narray1[idx] = element
narray2 = np.array(array2)
it = 0
idxs2 = []
for element in array2:
idx = randint(0,len(array2)-1)
while idx in idxs2 or idx == idxs[it]:
idx = randint(0,len(array2)-1)
idxs2.append(idx)
it+=1
narray2[idx] = element
return narray1, narray2
new1, new2 = my_shuffle(array1, array1)
print(f"{new1}\n{new2}")

Sum consecutive pairs of elements in a list

I want to find sums of elements of the list by index0,index1 and index1,index2 and index2,index3, and so on.
Like:
my_list = [7, 5, 9, 4, 7, 11]
sums = [12, 14, 13, 11, 18] # 7+5, 5+9, 9+4, 4+7, 7+11
You just have to iterate through the indices:
l = [7, 5, 9, 4, 7, 11]
res = [l[i] + l[i+1] for i in range(len(l)-1)]
print(res)
Output:
[12, 14, 13, 11, 18]
You can use zip and sum for a functional solution:
# don't shadow the built-in `list`
l = [7,5,9,4,7,11]
# generate all neighboring pairs
pairs = zip(l, l[1:])
# generate all sums of pairs
sums = list(map(sum, pairs))
print(sums) # [12, 14, 13, 11, 18]
This works fine :)
list=[7,5,9,4,7,11]
aspSum = []
i = 0
while i<len(list)-1:
aspSum.append(list[i]+list[i+1])
i+=1

Categories