1

Weird problem with arrays that I'd like to simplify.

I have a series of lists like:

list1 = 'abc'
list2 = 'def'
...
listN = 'xyz'

and I need to zip them together like:

[['a', 0, 0, ..., 0],
 ['b', 0, 0, ..., 0],
 ['c', 0, 0, ..., 0],
 [0, 'd', 0, ..., 0],
 [0, 'e', 0, ..., 0],
 [0, 'f', 0, ..., 0],
 [...],
 [0, 0, 0, ..., 'x'],
 [0, 0, 0, ..., 'y'],
 [0, 0, 0, ..., 'z']]

I have the additional constraint that numpy is not an option.

Is there a simple way of doing this possibly using itertools? (it doesn't need to be a list as it is later accessed like for r in row: for c in column:

now I'm doing this:

array1 = [[0 for i in range(n)] for j in range(m)]

step_i = 0

for i, a in enumerate([listA, listB, ..., listN]):
    for b in a:
        array1[step_i][I] = b
        step_i += 1

but it's super ugly!

accdias
  • 5,160
  • 3
  • 19
  • 31
  • Welcome to StackOverflow. If you have working code and you want a better way, consider checking out https://codereview.stackexchange.com/ too! – kojiro Jan 31 '20 at 15:26
  • 3
    why it's *super ugly*? it's very clean and simple code... IMO, anything you will do trying to make it "prettier" will just make it more complicated and un-readable... Best you can do is wrap it in a function and just call that wherever you need... – Tomerikoo Jan 31 '20 at 15:29
  • From Zen of Python: _"Simple is better than complex."_ Your code looks OK. Can't see any advantage on _"improving"_ it. – accdias Jan 31 '20 at 15:31
  • it is ugly since I need to initialise a potentially huge nd-array using 2 loops then loop again over all values accessing each individual value instead of concatenating the constituents using a default value to fill in the gaps. This is very inefficient code in terms of speed and memory without being more readable. In numpy I'd do some reshaping using the zeros_like function. – Vince Croft Jan 31 '20 at 15:38
  • Are the entries really single-character strings, or could they be arbitrary values? – chepner Jan 31 '20 at 15:43
  • Nooo they are complex generator objects that need to be configured in a particular order. the zeros are also special 'empty' objects that are simplified default entities of the other lists. – Vince Croft Jan 31 '20 at 15:52
  • Does this answer your question? [Diagonal stacking in numpy?](https://stackoverflow.com/questions/7158098/diagonal-stacking-in-numpy) – Grzegorz Skibinski Jan 31 '20 at 15:54
  • 1
    @GrzegorzSkibinski "I have the additional constraint that numpy is not an option." – felipe Jan 31 '20 at 15:56

3 Answers3

0

I think the only thing to make it look "less ugly" would be to replace the temporary array with a generator and get rid of step_i.

Here's what I managed to do:

def diag_zip(*lists):
    for i, a in enumerate(lists):
        for el in a:
            row = [0 for _x in range(len(lists))]
            row[i] = el
            yield row

if __name__ == '__main__':
    print(list(diag_zip('abc', 'def', 'ghi', 'xyz')))

It outputs this (formatting by me):

[['a', 0, 0, 0], ['b', 0, 0, 0], ['c', 0, 0, 0],
 [0, 'd', 0, 0], [0, 'e', 0, 0], [0, 'f', 0, 0],
 [0, 0, 'g', 0], [0, 0, 'h', 0], [0, 0, 'i', 0],
 [0, 0, 0, 'x'], [0, 0, 0, 'y'], [0, 0, 0, 'z']]
art-solopov
  • 4,289
  • 3
  • 25
  • 44
  • 1
    What do you think about `row = [0] * len(lists)`? (Or just `yield [x if n == i else 0 for n in range(len(lists))]`?) – folkol Jan 31 '20 at 15:51
0
from pprint import pprint
import string

# Generates the ['abc', 'def', ..., ] list.
abc = string.ascii_lowercase
three = lambda l, i: f"{l[i-1]}{l[i]}{l[i+1]}"
lists = [three(abc, i+1) for i in range(len(abc)) if i % 3 == 0 and i < 24]

data = []
for i, o in enumerate(lists):
    curr = [0]*len(lists)

    for obj in list(o):
        curr[i] = obj
        data.append(curr.copy())

pprint(data)

Or, if you want to have some fun, here is a one-liner:

data = [(lambda l, ind, obj: l[:ind] + [obj] + l[ind+1:])([0]*len(lists), i, letter) for i,o in enumerate(lists) for letter in list(o)]

Here is a self-encapsulating code that does everything (including generating the string + printing) in one line (please don't use this -- it's just for fun):

print((lambda lists: [(lambda l, ind, obj: l[:ind] + [obj] + l[ind+1:])([0]*len(lists), i, letter) for i,o in enumerate(lists) for letter in list(o)])([(lambda l, i: f"{l[i-1]}{l[i]}{l[i+1]}")("abcdefghijklmnopqrstuvwxyz", i+1) for i in range(len("abcdefghijklmnopqrstuvwxyz")) if i % 3 == 0 and i < 24]))

Outputs

[['a', 0, 0, 0, 0, 0, 0, 0],
 ['b', 0, 0, 0, 0, 0, 0, 0],
 ['c', 0, 0, 0, 0, 0, 0, 0],
 [0, 'd', 0, 0, 0, 0, 0, 0],
 [0, 'e', 0, 0, 0, 0, 0, 0],
 [0, 'f', 0, 0, 0, 0, 0, 0],
 [0, 0, 'g', 0, 0, 0, 0, 0],
 [0, 0, 'h', 0, 0, 0, 0, 0],
 [0, 0, 'i', 0, 0, 0, 0, 0],
 [0, 0, 0, 'j', 0, 0, 0, 0],
 [0, 0, 0, 'k', 0, 0, 0, 0],
 [0, 0, 0, 'l', 0, 0, 0, 0],
 [0, 0, 0, 0, 'm', 0, 0, 0],
 [0, 0, 0, 0, 'n', 0, 0, 0],
 [0, 0, 0, 0, 'o', 0, 0, 0],
 [0, 0, 0, 0, 0, 'p', 0, 0],
 [0, 0, 0, 0, 0, 'q', 0, 0],
 [0, 0, 0, 0, 0, 'r', 0, 0],
 [0, 0, 0, 0, 0, 0, 's', 0],
 [0, 0, 0, 0, 0, 0, 't', 0],
 [0, 0, 0, 0, 0, 0, 'u', 0],
 [0, 0, 0, 0, 0, 0, 0, 'v'],
 [0, 0, 0, 0, 0, 0, 0, 'w'],
 [0, 0, 0, 0, 0, 0, 0, 'x']]
felipe
  • 7,324
  • 2
  • 28
  • 37
0

First I would add the lists together like so:

lists = [list_1, list_2, list3...]

Now you can do a nested array:

output = []
for i in range(0, len(list)):
   for letter in list:
       #initialize array to be appended to output.
       temp = [0, 0, 0, 0, 0, 0, 0, 0]
       #replace index i with letter
       temp[i] = letter
       #add it to the output
       output.append(temp)


kingkyle
  • 111
  • 1
  • 8