1

I want to perform element-wise mathematical operations (e.g. sum, multiply..) on two Python lists containing numbers or multiple nested lists which may contain again numbers or lists and so on.

The shapes of the two lists are equal when performing the operation. Furthermore, the result should be of the same shape as the two input lists.

A shape may differ in:

  1. length,

  2. width (i.e. number of nestings),

  3. order (e.g. the lists start with a number followed by a nested list, but it may also be that it starts with a nested list, followed by numbers).

The shape changes arbitrarily each time I want to perform a mathematical operation.

How can I perform math operations on arbitrarily shaped lists?

In the past, I've implemented a customized piece of code for each different shape, similar to 1, 2, 3, 4, 5, 6, 7, 8, 9, but I was wondering if there is already a more general solution in the form of a library or reference code for this.

Example 1 (summation):

a = [ 1, 2, 3, 4,[ 5, 6, 7, 8]]
b = [10,20,30,40,[50,60,70,80]]
c = elementwiseSUM(a,b)
c

would result in

[11, 22, 33, 44, [55, 66, 77, 88]]

Example 2 (summation):

d = [ 1,[ 2, 3],  4, [ 5, 6, 7], [[ 8], [ 9, 10]]]
e = [10,[20,30], 40, [50,60,70], [[80], [90,100]]]
f = elementwiseSUM(d,e)
f

would result in

[11, [22, 33], 44, [55, 66, 77], [[88], [99, 110]]]

Example 3 (multiplication):

g = [[4,2],1,1]
h = [[8,3],1,9]
i = elementwiseMUL(g,h)
i

would result in

[[32, 6], 1, 9]

elementwiseSUM() and elementwiseMUL() are placeholders for the library functions which I am looking for.

Georgy
  • 12,464
  • 7
  • 65
  • 73
R3n3
  • 31
  • 6
  • How do you handle the situation when the shapes are not the same? – artemis Aug 22 '19 at 18:44
  • The shape of the two lists are always equal when performing the arithmetic operation.The only case which I can think of for which an arithmetic operation in a "general" way would make sense would be if one of the two lists would be/contain a single scalar. – R3n3 Aug 22 '19 at 18:55

4 Answers4

2

Here's the solution I came up with

a = [ 1,  2,  3,  4, [ 5,  6,  7,  8]]
b = [10, 20, 30, 40, [50, 60, 70, 80]]

def element_wise(a, b, f):
    return [element_wise(i, j, f) if type(i) == list and type(j) == list else f(i, j) for i, j in zip(a, b)]

c = element_wise(a, b, lambda x, y: x + y) # [11, 22, 33, 44, [55, 66, 77, 88]]

so a and b are your lists, and f is the function you want to apply, as you can see I wrote a simple function to add ints

You can either write your own lambdas or just use the operator module.

dangee1705
  • 3,445
  • 1
  • 21
  • 40
  • 1
    I like this one since its cleaner to call the function + more versatile with lambda. Although the 'and' doesn't seem to be needed, (only check one) as it will break no matter what if the list is an incorrect format. – Chrismon Chin Aug 22 '19 at 19:09
  • 1
    You're completely correct I just added the `and` for completeness really :) – dangee1705 Aug 22 '19 at 19:17
1

Unsure about any library that has a built in method, but using one wouldn't be any faster than creating one yourself

an example that is not any kind of production code but shows an easy way to do this is:

import operator
ops = {
    "+": operator.add,
    "-": operator.sub,
    "*": operator.mul,
    "/": operator.div
}

a = [ 1,[ 2, 3],  4, [ 5, 6, 7], [[ 8], [ 9, 10]]]
b = [10,[20,30], 40, [50,60,70], [[80], [90,100]]]


def foo(x, y, op):
    if type(x) == list:
        return [foo(a, b, op) for a, b in zip(x, y)]
    else:
        return ops[op](x, y)

#Run
print foo(a, b, '+')
Chrismon Chin
  • 429
  • 2
  • 12
0

It's not exactly taking advantage of a built-in python module (besides operator), but how about something like this?

def element_wise(list_a, list_b, operator):
    result = []
    assert len(list_a) == len(list_b)
    for a, b in zip(list_a, list_b):
        is_lists = isinstance(a, list) and isinstance(b, list)
        is_ints = isinstance(a, int) and isinstance(b, int)

        if is_lists:
            result.append(element_wise(a, b, operator))
        elif is_ints:
            result.append(operator(a, b))
        else:
            raise ValueError
    return result


def main():

    from operator import add, mul

    list_sum_a = [ 1,  2,  3,  4, [ 5,  6,  7,  8]]
    list_sum_b = [10, 20, 30, 40, [50, 60, 70, 80]]

    list_mul_a = [[4, 2], 1, 1]
    list_mul_b = [[8, 3], 1, 9]

    result_sum = element_wise(list_sum_a, list_sum_b, add)
    result_mul = element_wise(list_mul_a, list_mul_b, mul)

    print(result_sum)
    print(result_mul)

    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main())

Output:

[11, 22, 33, 44, [55, 66, 77, 88]]
[[32, 6], 1, 9]
Paul M.
  • 10,481
  • 2
  • 9
  • 15
0

I am giving the solution code for example one and three, But this solution is totally depends on your list i.e. How many list you have inside the list because i am using for loop and that depends on your list.

Example 1 (summation solution)

a = [ 1, 2, 3, 4,[ 5, 6, 7, 8]]
b = [10,20,30,40,[50,60,70,80]]   
def elementwiseSUM(a, b):
    pl = []
    cl = []
    for i, j in zip(a, b):
        if type(i) and type(j) != list:
            pl.append(i+j)
        if type(i) and type(j) == list:
            for k, l in zip(i, j):
                cl.append(k+l)
    pl.append(cl)
    return pl
print(elementwiseSUM(a, b))

Example 2 (multiplication solution)

g = [[4,2],1,1]
h = [[8,3],1,9]
def elementwiseMUL(g, h):
    pl = []
    cl = []
    for i, j in zip(g, h):
        if type(i) and type(j) != list:
            cl.append(i*j)
        if type(i) and type(j) == list:
            for k, l in zip(i, j):
                pl.append(k*l)
    pl.append(cl)
    return pl
print(elementwiseMUL(g, h))
Sarvesh
  • 99
  • 5