Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Python

Multi-dimensional Array Implementation in Python 3.2 besides with the coincidence of C#

3.88/5 (7 votes)
8 Mar 2012GPL32 min read 23.4K  
A multi dimensional array class with the advance of negative indexes. Used like a[-2, -5, 3] with any dimensions.

Download multiray.py.zip for Python 3.x[^]

Download multiray.cs.zip (updated 05.08.2011)[^]

Introduction

You can easily make use of Multiray class for issues regarding multidimensional arrays. Now you can download C# version of multiray completely with the same structure as python version.

Using the Code

The usage is simple:
  1. Initialize Multiray object with predefined dimensions:
    Python
    a = Multiray(2, 5, 20, 6);
    C#
    using multiray;
    MultiRay a = new MultiRay(2, 5, 20, 6);
  2. Assign your data values to multi-index specified cells:
    Python
    #a[1, 4, 21, 1] will assert an overflow.
    a[1, 2, 18, 5] = "Any Object";
    C#
    //a[1, 4, 21, 1] will raise an exception.
    a[1, 2, 18, 5] = "Any Object";
  3. Looping is somewhat like jagged arrays. Use GetLength(d) for any dimension with index d.
    Python
    for i in range(a.GetLength(0)):
      for j in range(a.GetLength(1)):
        for k in range(a.GetLength(2)):
          for l in range(a.GetLength(3)):
            #do anything you want with
            #a[i, j, k, l];
            print("a[{1:3}]={0}".format(a[i, j, k, l], a.Position));
    C#
    for (int i = 0; i < a.GetLength(0); i++)
        for (int j = 0; j < a.GetLength(1); j++)
            for (int k = 0; k < a.GetLength(2); k++)
                for (int l = 0; l < a.GetLength(3); l++)
                {
                    a[i, j, k, l] = "Hello World";
                    Console.WriteLine("a[{0},{1},{2},{3}] = {4}"
                        , i, j, k, l, a[i, j, k, l]);
                }

_EvaluateIndex gets real index of given indexes-tuple with this principle:

  • if a is a (I x J x K) three dimensional array; let a[i][j][k] nth item in a array. In Multiray implementation, n is evaluated by _EvaluateIndex() method call.
  • for D = 3, n is thus:
    • n = i*J*K + j*K + k;
  • for D = 4(I x J x K x T :: i, j, k, t) n is thus:
    • n = i*J*K*T + j*K*T + k*K + t;
  • We multiply the first key with J, K and T; second key with K and T; third with T and fourth with 1
  • So we need to multiply first index with keys[1]*keys[2]*keys[3] second key with keys[2]*keys[3] and so on...
  • _EvaluateIndex definitely do this:
    Python
    def _EvaluateIndex(self, keys, k):
        dLen = len(self.Dimensions);
        #attention!
        #check recursive deadline:
        if k == dLen: return keys[-1];
        t = 1; #accumulator
        #multiply t with the given dimensions k to dLen
        for i in range(k, dLen):
          t *= self.Dimensions[i]
        t *= keys[k - 1];
        return t + self._EvaluateIndex(keys, k=k+1);
    C#
    private int _EvaluateIndex(int[] keyTuple, int k)
    {
        int dLen = this.Dimensions.Length;
        if (k == dLen)
            return keyTuple[keyTuple.Length - 1];
        int t = 1;
        for (int i = k; i < dLen; i++)
        {
            t *= this.Dimensions[i];
        }
        t *= keyTuple[k - 1];
        return t + this._EvaluateIndex(keyTuple, k + 1);
    }

How is it possible using Multiray object with an indexer?

  • Solution is implementing __getitem__ and __setitem__:
    Python
    def __getitem__(self, keyTuple):
        keys = self._CheckKeys(keyTuple);
        self.Position = self._EvaluateIndex(keys, k=1);
        return self._Arr2D[self.Position];
  • self._CheckKeys makes checks and conversion of minus indexes(a[2,3,-2]); then self.Position is evaluated and is given as index to the self._Arr2D array.
    Python
    def __setitem__(self, keyTuple, item):
        keys = self._CheckKeys(keyTuple);
        self.Position = self._EvaluateIndex(keys, k=1);
        self._Arr2D[self.Position] = item
    C#
    public object this[params int[] keyTuple]
    {
        get
        {
            int[] keys = this._CheckKeys(keyTuple);
            this.Position = this._EvaluateIndex(keys, 1);
            return this._Arr2D[this.Position];
        }
        set
        {
            int[] keys = this._CheckKeys(keyTuple);
            this.Position = this._EvaluateIndex(keys, 1);
            this._Arr2D[this.Position] = value;
        }
    }
  • Again self._CheckKeys() makes checks and manipulations:
    Python
    def _CheckKeys(self, keyTuple):
        assert(len(keyTuple) == len(self.Dimensions));
        keyList = list(keyTuple);
        limit = len(keyList);
        for i in range(limit):
          assert(keyList[i] < self.Dimensions[i] \
                 and keyList[i] >= -self.Dimensions[i]);
          #for minus indexes:
          #like a[1,-2]
          if keyList[i] < 0:
            keyList[i] += self.Dimensions[i];
        return keyList;
    C#
    private int[] _CheckKeys(int[] keyTuple)
    {
        Debug.Assert(keyTuple.Length == this.Dimensions.Length);
        int limit = keyTuple.Length;
        int[] keyList = new int[limit];
        for (int i = 0; i < limit; i++)
        {
            Debug.Assert(keyList[i] < this.Dimensions[i]
                && keyList[i] >= -this.Dimensions[i]);
            keyList[i] = keyTuple[i];
            //for negative indexes
            //like a[1,-2]
            if (keyList[i] < 0)
                keyList[i] += this.Dimensions[i];
        }
        return keyList;
    }

Last word

  • I found out that "," delimeted indexes are passed in tuples. So when I use a[1,2,3] __getitem__ takes (1,2,3) tuple.
  • So positional arguments not needed for now.

Points of Interest

I suffered from invalid mathematical evaluations. By the help of induction, I could eventually implement the right way of function.

History

The previous version lacked c sharp in advance.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)