简体   繁体   中英

How do I switch rows and columns in a 2D array?

I am working on a code that will create a visual Sierpinski triangle to 3D print, and in order for it to work I have to use a Pascal triangle algorithm that will create an array so I can use to tell my algorithm that will create my triangles where not to put triangles.

Anyway the problem is, that my code that arranges the triangles creates the triangles by column instead of by row like the Pascal algorithm does so I am just trying to do a quick fix by having a subroutine that rearranges the Pascal array. I am just stumped on how to do it since I am not sure how to avoid index out of range errors.

This is the code that creates the array for Pascal's triangle.

TL:DR I am trying to make an rearrange an array where rows are the columns and columns are the rows

def pascal(n):
    """Prints out n rows of Pascal's triangle."""
    row = [1]
    global array
    array = [[0 for x in range(int(n))] for y in range(int(n))]
    array[0]=row
    k = [0]
    for x in range(int(max(n,0)-1)):
       row=[l+r for l,r in zip(row+k,k+row)]
       array[x+1]=row
    return 1

This is the output of printing the array. I just want the rows to be columns and columns to be rows

[[1],
 [1, 1],
 [1, 2, 1],
 [1, 3, 3, 1],
 [1, 4, 6, 4, 1],
 [1, 5, 10, 10, 5, 1],
 [1, 6, 15, 20, 15, 6, 1],
 [1, 7, 21, 35, 35, 21, 7, 1]]

Here is the full code if you are curious about the project, but it requires rhinoscriptsyntax to make the model.

import rhinoscriptsyntax as rhino
import math

obj = rhino.GetObject("Select object to transform", preselect=True)
scale = 3
n=math.pow(3,scale)
def pascal(n):
   """Prints out n rows of Pascal's triangle."""
   row = [1]
   global array
   array = [[0 for x in range(int(n))] for y in range(int(n))]
   array[0]=row
   k = [0]
   for x in range(int(max(n,0)-1)):
      row=[l+r for l,r in zip(row+k,k+row)]
      array[x+1]=row
   return 1
pascal(math.pow(2,scale))
print array
def remakePascal():
    pass
my_horizontalVector = [[1,0,0,6],
           [0,1,0,0],
           [0,0,1,0],
           [0,0,0,1]]

my_tsfm = [[1,0,0,0],  #identity
           [0,1,0,0],
           [0,0,1,0],
           [0,0,0,1]]


def makeTriangle(scale,obj):
    w=1/scale
    h=1/scale
    tsfm= [[w,0,0,0],   #scale about origin
            [0,h,0,0],
            [0,0,1,0],
            [0,0,0,1]]
    output= rhino.XformMultiply(my_tsfm,tsfm)
    new_obj=rhino.TransformObject(obj,output,copy=True)
    return new_obj
def placeObj(i):
    my_moveUpVector = [[1,0,0,(3/scale)*i],
           [0,1,0,(4/scale)*i],
           [0,0,1,0],
           [0,0,0,1]]
    vector = rhino.XformMultiply(my_tsfm,my_moveUpVector)
    return vector
n=0
for i in range(int(math.pow(2,scale))):
    if(i>0):
       hPlace=rhino.XformMultiply(my_tsfm,my_horizontalVector)
       obj = rhino.TransformObject(obj,hPlace)
    factor = int(math.pow(2,scale))-n
    for j in range(factor):
        if():
            pass
        else:
            Vertobj=makeTriangle(scale,obj)
            tsfm = rhino.TransformObject(Vertobj,placeObj(j),copy=True)
    n=n+1

For transposing square arrays, the simple solution is

transposed_array = zip(*array)

This approach is not suitable for triangular data as zip does not insert padding when the rows are of unequal length. itertools.izip_longest does, however:

import itertools
transposed_array = itertools.izip_longest(*array)

By default izip_longest pads with None , so you get results like:

[(1, 1, 1, 1, 1),
 (None, 1, 2, 3, 4),
 (None, None, 1, 3, 6),
 (None, None, None, 1, 4),
 (None, None, None, None, 1)]

If you wish, you can remove the None entries with a list comprehension:

no_nones = [[item for item in row if item is not None] for row in transposed_array]

which leaves you with:

[[1, 1, 1, 1, 1],
 [1, 2, 3, 4],
 [1, 3, 6],
 [1, 4],
 [1]]

If you prefer to pad with 0s (or anything else), specify that as the fillvalue keyword argument to itertools.izip_longest ; eg

list(itertools.izip_longest(*array, fillvalue=0))

returns

[(1, 1, 1, 1, 1),
 (0, 1, 2, 3, 4),
 (0, 0, 1, 3, 6),
 (0, 0, 0, 1, 4),
 (0, 0, 0, 0, 1)]

I'm assuming here that a list of tuples works for your purposes. If you need the inside to mutable, you can get that with a list comprehension, eg:

list_of_lists = [list(row) for row in transposed_array]

You probably want something like this:

def pascal(n):
    a = [[int(i==0) for j in range(n)] for i in range(n)]
    for i in range(n):
        for j in range(1, 1+i):
            a[j][i] = a[j][i-1] + a[j-1][i-1]

    for line in a: print(line)

pascal(7)

this prints:

[1, 1, 1, 1, 1, 1, 1]
[0, 1, 2, 3, 4, 5, 6]
[0, 0, 1, 3, 6, 10, 15]
[0, 0, 0, 1, 4, 10, 20]
[0, 0, 0, 0, 1, 5, 15]
[0, 0, 0, 0, 0, 1, 6]
[0, 0, 0, 0, 0, 0, 1]

You could do it as shown. It works by first making the array square so that all the rows have the same number of elements. then transposes the rows and columns using the built-in zip() function, and then finally removes the elements it initially added.

Note also I removed the use of the global variable array . Global variables are best avoided.

from pprint import pprint

def pascal(n):
    """Creates n rows of Pascal's triangle."""
    array = [None for y in range(n)]

    row = [1]
    array[0] = row
    k = [0]
    for x in range(max(n, 0)-1):
       row = [l+r for l,r in zip(row+k, k+row)]
       array[x+1] = row

    return array

def transpose(array):
    array = array[:]  # make copy to avoid changing original
    n = len(array)
    for i, row in enumerate(array):
        array[i] = row + [None for _ in range(n - len(row))]

    array = zip(*array)

    for i, row in enumerate(array):
        array[i] = [elem for elem in row if elem is not None]

    return array

array = pascal(8)
array = transpose(array)
pprint(array)

Output:

[[1, 1, 1, 1, 1, 1, 1, 1],
 [1, 2, 3, 4, 5, 6, 7],
 [1, 3, 6, 10, 15, 21],
 [1, 4, 10, 20, 35],
 [1, 5, 15, 35],
 [1, 6, 21],
 [1, 7],
 [1]]

You can transpose the rows and columns after matching length of each rows with max row and filling with zeros.

Working code as below. numpy_fillna function sourced from this SO post .

import numpy as np
x = [[1],
 [1, 1],
 [1, 2, 1],
 [1, 3, 3, 1],
 [1, 4, 6, 4, 1],
 [1, 5, 10, 10, 5, 1],
 [1, 6, 15, 20, 15, 6, 1],
 [1, 7, 21, 35, 35, 21, 7, 1]]

a = np.array(x)

def numpy_fillna(data):
    # Get lengths of each row of data
    lens = np.array([len(i) for i in data])

    # Mask of valid places in each row
    mask = np.arange(lens.max()) < lens[:,None]

    # Setup output array and put elements from data into masked positions
    out = np.zeros(mask.shape, dtype=data.dtype)
    out[mask] = np.concatenate(data)
    return out

Result

>>> numpy_fillna(a).T
array([[1, 1, 1, 1, 1, 1, 1, 1],
       [0, 1, 2, 3, 4, 5, 6, 7],
       [0, 0, 1, 3, 6, 10, 15, 21],
       [0, 0, 0, 1, 4, 10, 20, 35],
       [0, 0, 0, 0, 1, 5, 15, 35],
       [0, 0, 0, 0, 0, 1, 6, 21],
       [0, 0, 0, 0, 0, 0, 1, 7],
       [0, 0, 0, 0, 0, 0, 0, 1]], dtype=object)
>>> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM