Welcome, guest | Sign In | My Account | Store | Cart

Two functions useful when you don't use a numerical library. The first one creates a tensor, hopefully in the correct way, avoiding the mutability trap. The second one transposes a 2D matrix keeping the type of the lists/tuples used.

Python, 97 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
from types import NoneType # for tensor
from copy import deepcopy # for tensor


def tensor(sizes=0, elem=0):
    """tensor(sizes=0, elem=0): creates a list of lists of lists...
    The parameter sizes can be a number or a tuple/list or sizes.

    >>> tensor()
    []
    >>> tensor(())
    []

    It works with a single number or a sequence of numbers:
    >>> tensor(3)
    [0, 0, 0]
    >>> tensor((3))
    [0, 0, 0]
    >>> tensor((3), None)
    [None, None, None]
    >>> tensor((2, 3)) # array of 2 rows and 3 colums.
    [[0, 0, 0], [0, 0, 0]]
    >>> tensor((2, 3), 2)
    [[2, 2, 2], [2, 2, 2]]
    >>> tensor((1, 2, 3))
    [[[0, 0, 0], [0, 0, 0]]]
    >>> tensor((2, 2, 3))
    [[[0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]]]

    It works with mutables too, calling deepcopy:
    >>> r = tensor((2, 3), [3])
    >>> r
    [[[3], [3], [3]], [[3], [3], [3]]]
    >>> r[0][2][0] = 0
    >>> r
    [[[3], [3], [0]], [[3], [3], [3]]]
    """
    if isinstance(sizes, (int, long)):
        sizes = [sizes]
    elif not isinstance(sizes, list):
        sizes = list(sizes)

    result = []
    if sizes:
        first = sizes.pop()
        if isinstance(elem, (int, long, basestring, tuple, NoneType, bool)):
            result = [elem] * first
        else:
            result = [deepcopy(elem) for i in xrange(first)]
        while sizes:
            result = [deepcopy(result) for i in xrange(sizes.pop())]
    return result


def transpose(m):
    """transpose(m): transposes a 2D matrix, made of tuples or lists of tuples or lists,
    keeping their type.

    >>> transpose([])
    Traceback (most recent call last):
      ...
    IndexError: list index out of range
    >>> transpose([[]])
    []
    >>> transpose([1,2,3])
    Traceback (most recent call last):
      ...
    TypeError: zip argument #1 must support iteration
    >>> transpose([[1,2,3]])
    [[1], [2], [3]]
    >>> transpose( [[2, 2, 2], [2, 2, 2]] )
    [[2, 2], [2, 2], [2, 2]]
    >>> transpose( [(2, 2, 2), (2, 2, 2)] )
    [(2, 2), (2, 2), (2, 2)]
    >>> transpose( ([2, 2, 2], [2, 2, 2]) )
    ([2, 2], [2, 2], [2, 2])
    >>> transpose( ((2, 2, 2), (2, 2, 2)) )
    ((2, 2), (2, 2), (2, 2))
    >>> t = [[[1], [2]], [[3], [4]], [[5], [6]]]
    >>> transpose(t)
    [[[1], [3], [5]], [[2], [4], [6]]]
    """
    if isinstance(m, list):
        if isinstance(m[0], list):
            return map(list, zip(*m))
        else:
            return zip(*m) # faster
    else:
        if isinstance(m[0], list):
            return tuple(map(list, zip(*m)))
        else:
            return tuple( zip(*m) )

if __name__ == "__main__":
    import doctest
    doctest.testmod()
    print "Tests done."

1 comment

kay schluehr 15 years, 2 months ago  # | flag

Deepcopy. I really don't like deepcopying values using the deepcopy() function. It slows your algorithm down and is kind of a last resort. Although the simplicity of the algorithm may be destroyed I would suggest more case analysis. For instance if L is a list of immutables it is appropriate to clone it using K = L[:]. One might even think about using a lazy datatype, that creates columns on demand ( depends on usage of course ).