List to Mixed dimension Matrix - python

I have been trying to figure out how to take a list and match the shape of any matrice.
a = [1,2,3,4,5,6,7,8,9]
b = [[3],[[2]],[1,8,2],[[[7,3,2]]],[9]]
I want list a to match the dimensions of matrix b. Both a and b have the same number of individual elements. The function requires a and be to have the same number of elements.
output = [[1],[[2]],[3,4,5],[[[[6,7,8]],[9]]
My attemp at trying to do this.
import numpy as np
lst = []
start = 0
def match_matrix(a,b):
for k, v in enumerate(b):
shape = np.shape(v)
lst.append(np.reshape(a[start:start+element_num], newshape= shape) # there problem is here, how would I figure out how many elements in any dimension matrix
start += element_num
return lst

Not sure, why you want to use numpy for this. Here is a simple solution to create a list of lists
>>> a = [1,2,3,4,5,6,7,8,9]
>>> b = [[3],[[2]],[1,8,2],[[[7,3,2]]],[9]]
>>>
>>> def create_list(l, itr):
... return [create_list(e, itr) if isinstance(e, list) else next(itr) for e in l]
...
>>>
>>> create_list(b, iter(a))
[[1], [[2]], [3, 4, 5], [[[6, 7, 8]]], [9]]

This can be done straight-forwardly with recursion. This function assumes that list b has the same number of non-list elements than the length of list a
def match_matrix(a, b):
it = iter(a)
def _worker(b):
lst = []
for x in b:
if isinstance(x, list):
lst.append(_worker(x))
else:
lst.append(next(it))
return lst
return _worker(b)

Related

remove pairs of equal elements from two lists in sympy

I am quite new to Sympy (running with Python 3).
Given two lists of integers, in general not of equal lengths and whose elements might repeat, I need to eliminate all pairs of equal elements between the two lists.
For example, given
List_a=[1, 2, 3, 4, 4], List_b=[0, 2, 2, 4]
I shall eliminate the pairs (2,2) and (4,4) and output
List_ar=[1,3,4], List_br=[0,2]
If the two lists are equal I shall get two empty lists as output.
I have been trying to compare the lists element by element ( "for" and "while" loops) and when found equal to delete the pair from both lists. Upon that to repeat the procedure on the reduced lists till no deletion is made.
But lacking of a "goto" control I do not know how to handle the variable length lists.
Thanks for helping.
I you convert them to multiset you can compare the counts of common keys:
>>> from sympy.utilities.iterables import multiset
>>> ma = multiset(lista)
>>> mb = multiset(listb)
>>> for k in set(ma) & set(mb):
... n = min(ma[k],mb[k])
... ma[k] -= n
... mb[k] -= n
...
>>> lista = [i for k in ma for i in [k]*ma[k]]
>>> listb = [i for k in mb for i in [k]*mb[k]]
You could also treat this like a merge sort but the above is direct.
A general Python solution:
def g(a, b):
def ensure2(xs):
ys = [list(x) for x in xs]
if ys == []:
return [[], []]
else:
return ys
n = min(len(a), len(b))
c, d = ensure2(
zip(*
filter(lambda x: x[0] != x[1],
zip(a, b))))
return c + a[n:], d + b[n:]
a = [1,2,3,4,4]
b = [0,2,2,4]
c = [0,2,2,4]
d = []
# two different lists
ar, br = g(a, b)
print(ar) # [1, 3, 4]
print(br) # [0, 2]
# two identical lists
br, cr = g(b, c)
print(br) # []
print(cr) # []
# one empty list
ar, dr = g(a, d)
print(ar) # [1, 2, 3, 4, 4]
print(dr) # []
Use zip() to create the pairs
Use filter() to remove pairs with equal elements
Use zip(*...) to split the remaining pairs back to two lists
Use ensure2() to guard against empty lists
Use + list[n:] to append excessive elements back to the originally longer list

Group repeated elements of a list

I am trying to create a function that receives a list and return another list with the repeated elements.
For example for the input A = [2,2,1,1,3,2] (the list is not sorted) and the function would return result = [[1,1], [2,2,2]]. The result doesn't need to be sorted.
I already did it in Wolfram Mathematica but now I have to translate it to python3, Mathematica has some functions like Select, Map and Split that makes it very simple without using long loops with a lot of instructions.
result = [[x] * A.count(x) for x in set(A) if A.count(x) > 1]
Simple approach:
def grpBySameConsecutiveItem(l):
rv= []
last = None
for elem in l:
if last == None:
last = [elem]
continue
if elem == last[0]:
last.append(elem)
continue
if len(last) > 1:
rv.append(last)
last = [elem]
return rv
print grpBySameConsecutiveItem([1,2,1,1,1,2,2,3,4,4,4,4,5,4])
Output:
[[1, 1, 1], [2, 2], [4, 4, 4, 4]]
You can sort your output afterwards if you want to have it sorted or sort your inputlist , then you wouldnt get consecutive identical numbers any longer though.
See this https://stackoverflow.com/a/4174955/7505395 for how to sort lists of lists depending on an index (just use 0) as all your inner lists are identical.
You could also use itertools - it hast things like TakeWhile - that looks much smarter if used
This will ignore consecutive ones, and just collect them all:
def grpByValue(lis):
d = {}
for key in lis:
if key in d:
d[key] += 1
else:
d[key] = 1
print(d)
rv = []
for k in d:
if (d[k]<2):
continue
rv.append([])
for n in range(0,d[k]):
rv[-1].append(k)
return rv
data = [1,2,1,1,1,2,2,3,4,4,4,4,5,4]
print grpByValue(data)
Output:
[[1, 1, 1, 1], [2, 2, 2], [4, 4, 4, 4, 4]]
You could do this with a list comprehension:
A = [1,1,1,2,2,3,3,3]
B = []
[B.append([n]*A.count(n)) for n in A if B.count([n]*A.count(n)) == 0]
outputs [[1,1,1],[2,2],[3,3,3]]
Or more pythonically:
A = [1,2,2,3,4,1,1,2,2,2,3,3,4,4,4]
B = []
for n in A:
if B.count([n]*A.count(n)) == 0:
B.append([n]*A.count(n))
outputs [[1,1,1],[2,2,2,2,2],[3,3,3],[4,4,4,4]]
Works with sorted or unsorted list, if you need to sort the list before hand you can do for n in sorted(A)
This is a job for Counter(). Iterating over each element, x, and checking A.count(x) has a O(N^2) complexity. Counter() will count how many times each element exists in your iterable in one pass and then you can generate your result by iterating over that dictionary.
>>> from collections import Counter
>>> A = [2,2,1,1,3,2]
>>> counts = Counter(A)
>>> result = [[key] * value for key, value in counts.items() if value > 1]
>>> result
[[2, 2, 2], [[1, 1]]

Summing a list of lists of lists?

I'm trying to sum a list of lists of lists in Python but I'm getting the wrong output. I want the number 36 as my answer but I'm getting the sum of each bracket.
>>> list = [[[1,2],[3,4]],[[5,6],[7,8]]]
>>> for xs in list[0::1]:
... for x in xs[0::1]:
... sum(x)
...
3
7
11
15
You could probably do this recursively, which would work for nested lists of arbitrary depth:
def add_all(l):
try:
return sum(add_all(i) for i in l)
except TypeError:
return l
print add_all([[[1,2],[3,4]],[[5,6],[7,8]]]) # 36
Here's a little more fancy way to do it:
You can use itertools.chain to remove one level of nesting from your list of lists:
>>> lst = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
>>> from itertools import chain
>>> list(chain(*lst)) # note: list != lst
[[1, 2], [3, 4], [5, 6], [7, 8]]
Now apply it twice and sum all items:
>>> sum(chain(*chain(*lst)))
36
You can also use list comprehension like this -
>>> lst = [[[1,2],[3,4]],[[5,6],[7,8]]]
>>> sum([x for i in lst for y in i for x in y])
36
Or
>>> sum(sum(y) for x in lst for y in x)
36
Also, just FYI list is a bad name for a variable, since it overwrites the built-in list function.
If there are n nested lists (arbitrary number) I cannot think of a way to achieve the sum through list comprehension , but a simple recursive algorithm that would do the trick is -
>>> def sumOfList(element):
... if isinstance(element, list):
... return sum([sumOfList(x) for x in element])
... elif isinstance(element, int):
... return element
...
>>> sumOfList([[[1,2,3,4],[1,2,3,4]],[1,2,3]])
26
You can create a recursive function like this:
def rsum(lst):
if type(lst) != list:
return lst
if len(lst)==1:
return rsum(lst[0])
return rsum(lst.pop(0))+rsum(lst)
The difference is that it works for a nested list of any depth
Assign sublists sums to a variable:
total = 0
for x in list: # x is list of lists
for y in x: # y is a list
total = total + sum(y)
print total

How to find elements existing in two lists but with different indexes

I have two lists of the same length which contains a variety of different elements. I'm trying to compare them to find the number of elements which exist in both lists, but have different indexes.
Here are some example inputs/outputs to demonstrate what I mean:
>>> compare([1, 2, 3, 4], [4, 3, 2, 1])
4
>>> compare([1, 2, 3], [1, 2, 3])
0
# Each item in the first list has the same index in the other
>>> compare([1, 2, 4, 4], [1, 4, 4, 2])
2
# The 3rd '4' in both lists don't count, since they have the same indexes
>>> compare([1, 2, 3, 3], [5, 3, 5, 5])
1
# Duplicates don't count
The lists are always the same size.
This is the algorithm I have so far:
def compare(list1, list2):
# Eliminate any direct matches
list1 = [a for (a, b) in zip(list1, list2) if a != b]
list2 = [b for (a, b) in zip(list1, list2) if a != b]
out = 0
for possible in list1:
if possible in list2:
index = list2.index(possible)
del list2[index]
out += 1
return out
Is there a more concise and eloquent way to do the same thing?
This python function does hold for the examples you provided:
def compare(list1, list2):
D = {e:i for i, e in enumerate(list1)}
return len(set(e for i, e in enumerate(list2) if D.get(e) not in (None, i)))
since duplicates don't count, you can use sets to find only the elements in each list. A set only holds unique elements. Then select only the elements shared between both using list.index
def compare(l1, l2):
s1, s2 = set(l1), set(l2)
shared = s1 & s2 # intersection, only the elements in both
return len([e for e in shared if l1.index(e) != l2.index(e)])
You can actually bring this down to a one-liner if you want
def compare(l1, l2):
return len([e for e in set(l1) & set(l2) if l1.index(e) != l2.index(e)])
Alternative:
Functionally you can use the reduce builtin (in python3, you have to do from functools import reduce first). This avoids construction of the list which saves excess memory usage. It uses a lambda function to do the work.
def compare(l1, l2):
return reduce(lambda acc, e: acc + int(l1.index(e) != l2.index(e)),
set(l1) & set(l2), 0)
A brief explanation:
reduce is a functional programming contruct that reduces an iterable to a single item traditionally. Here we use reduce to reduce the set intersection to a single value.
lambda functions are anonymous functions. Saying lambda x, y: x + 1 is like saying def func(x, y): return x + y except that the function has no name. reduce takes a function as its first argument. The first argument a the lambda receives when used with reduce is the result of the previous function, the accumulator.
set(l1) & set(l2) is a set consisting of unique elements that are in both l1 and l2. It is iterated over, and each element is taken out one at a time and used as the second argument to the lambda function.
0 is the initial value for the accumulator. We use this since we assume there are 0 shared elements with different indices to start.
I dont claim it is the simplest answer, but it is a one-liner.
import numpy as np
import itertools
l1 = [1, 2, 3, 4]
l2 = [1, 3, 2, 4]
print len(np.unique(list(itertools.chain.from_iterable([[a,b] for a,b in zip(l1,l2) if a!= b]))))
I explain:
[[a,b] for a,b in zip(l1,l2) if a!= b]
is the list of couples from zip(l1,l2) with different items. Number of elements in this list is number of positions where items at same position differ between the two lists.
Then, list(itertools.chain.from_iterable() is for merging component lists of a list. For instance :
>>> list(itertools.chain.from_iterable([[3,2,5],[5,6],[7,5,3,1]]))
[3, 2, 5, 5, 6, 7, 5, 3, 1]
Then, discard duplicates with np.unique(), and take len().

Pythonic way to check that the lengths of lots of lists are the same

I have a number of lists that I'm going to use in my program, but I need to be sure that they are all the same length, or I'm going to get problems later on in my code.
What's the best way to do this in Python?
For example, if I have three lists:
a = [1, 2, 3]
b = ['a', 'b']
c = [5, 6, 7]
I could do something like:
l = [len(a), len(b), len(c)]
if max(l) == min(l):
# They're the same
Is there a better or more Pythonic way to do this?
Assuming you have a non-empty list of lists, e.g.
my_list = [[1, 2, 3], ['a', 'b'], [5, 6, 7]]
you could use
n = len(my_list[0])
if all(len(x) == n for x in my_list):
# whatever
This will short-circuit, so it will stop checking when the first list with a wrong length is encountered.
len(set(len(x) for x in l)) <= 1
Latter I ended up writing:
def some(x):
"""Replacement for len(set(x)) > 1"""
if isinstance(x, (set, frozenset)):
return len(x) > 1
s = set()
for e in x:
s.add(e)
if len(s) > 1:
return True
return False
def lone(x):
"""Replacement for len(set(x)) <= 1"""
return not some(x)
Which allows the above to be written as:
lone(len(x) for x in l)
This will stop taking the lengths of the lists as soon as it finds a list with a different length.
A bit of functional Python:
>>> len(set(map(len, (a, b, c)))) == 1
False
Each call to max and min will traverse the whole list, but you don't really need to do that; you can check for the desired property with one traversal:
def allsamelength(lst_of_lsts):
if len(lst_of_lsts) in (0,1): return True
lfst = len(lst_of_lsts[0])
return all(len(lst) == lfst for lst in lst_of_lsts[1:])
This will also short-circuit if one of the lists has a different length from the first.
If l is list of lengths:
l = [len(a), len(b), len(c)]
if len(set(l))==1:
print 'Yay. List lengths are same.'
Otherwise, using the original lists, one could create a list of lists:
d=[a,b,c]
if len(set(len(x) for x in d)) ==1:
print 'Yay. List lengths are same.'

Categories