Complex matlab-like data structure in python (numpy/scipy) - python

I have data currently structured as following in Matlab
item{i}.attribute1(2,j)
Where item is a cell from i = 1 .. n each containing the data structure of multiple attributes each a matrix of size 2,j where j = 1 .. m. The number of attributes is not fixed.
I have to translate this data structure to python, but I am new to numpy and python lists. What is the best way of structuring this data in python with numpy/scipy?
Thanks.

I've often seen the following conversion approaches:
matlab array -> python numpy array
matlab cell array -> python list
matlab structure -> python dict
So in your case that would correspond to a python list containing dicts, which themselves contain numpy arrays as entries
item[i]['attribute1'][2,j]
Note
Don't forget the 0-indexing in python!
[Update]
Additional: Use of classes
Further to the simple conversion given above, you could also define a dummy class, e.g.
class structtype():
pass
This allows the following type of usage:
>> s1 = structtype()
>> print s1.a
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-40-7734865fddd4> in <module>()
----> 1 print s1.a
AttributeError: structtype instance has no attribute 'a'
>> s1.a=10
>> print s1.a
10
Your example in this case becomes, e.g.
>> item = [ structtype() for i in range(10)]
>> item[9].a = numpy.array([1,2,3])
>> item[9].a[1]
2

A simple version of the answer by #dbouz , using the idea by #jmetz
class structtype():
def __init__(self,**kwargs):
self.Set(**kwargs)
def Set(self,**kwargs):
self.__dict__.update(kwargs)
def SetAttr(self,lab,val):
self.__dict__[lab] = val
then you can do
myst = structtype(a=1,b=2,c=3)
or
myst = structtype()
myst.Set(a=1,b=2,c=3)
and still do
myst.d = 4 # here, myst.a=1, myst.b=2, myst.c=3, myst.d=4
or even
myst = structtype(a=1,b=2,c=3)
lab = 'a'
myst.SetAttr(lab,10) # a=10,b=2,c=3 ... equivalent to myst.(lab)=10 in MATLAB
and you get exactly what you'd expect in matlab for myst=struct('a',1,'b',2,'c',3).
The equivalent of a cell of structs would be a list of structtype
mystarr = [ structtype(a=1,b=2) for n in range(10) ]
which would give you
mystarr[0].a # == 1
mystarr[0].b # == 2

If you are looking for a good example how to create a structured array in Python like it is done in MATLAB, you might want to have a look at the scipy homepage (basics.rec).
Example
x = np.zeros(1, dtype = [('Table', float64, (2, 2)),
('Number', float),
('String', '|S10')])
# Populate the array
x['Table'] = [1, 2]
x['Number'] = 23.5
x['String'] = 'Stringli'
# See what is written to the array
print(x)
The printed output is then:
[([[1.0, 2.0], [1.0, 2.0]], 23.5, 'Stringli')]
Unfortunately, I did not find out how you can define a structured array without knowing the size of the structured array. You can also define the array directly with its contents.
x = np.array(([[1, 2], [1, 2]], 23.5, 'Stringli'),
dtype = [('Table', float64, (2, 2)),
('Number', float),
('String', '|S10')])
# Same result as above but less code (if you know the contents in advance)
print(x)

For some applications a dict or list of dictionaries will suffice. However, if you really want to emulate a MATLAB struct in Python, you have to take advantage of its OOP and form your own struct-like class.
This is a simple example for instance that allows you to store an arbitrary amount of variables as attributes and can be also initialized as empty (Python 3.x only). i is the indexer that shows how many attributes are stored inside the object:
class Struct:
def __init__(self, *args, prefix='arg'): # constructor
self.prefix = prefix
if len(args) == 0:
self.i = 0
else:
i=0
for arg in args:
i+=1
arg_str = prefix + str(i)
# store arguments as attributes
setattr(self, arg_str, arg) #self.arg1 = <value>
self.i = i
def add(self, arg):
self.i += 1
arg_str = self.prefix + str(self.i)
setattr(self, arg_str, arg)
You can initialise it empty (i=0), or populate it with initial attributes. You can then add attributes at will. Trying the following:
b = Struct(5, -99.99, [1,5,15,20], 'sample', {'key1':5, 'key2':-100})
b.add(150.0001)
print(b.__dict__)
print(type(b.arg3))
print(b.arg3[0:2])
print(b.arg5['key1'])
c = Struct(prefix='foo')
print(c.i) # empty Struct
c.add(500) # add a value as foo1
print(c.__dict__)
will get you these results for object b:
{'prefix': 'arg', 'arg1': 5, 'arg2': -99.99, 'arg3': [1, 5, 15, 20], 'arg4': 'sample', 'arg5': {'key1': 5, 'key2': -100}, 'i': 6, 'arg6': 150.0001}
<class 'list'>
[1, 5]
5
and for object c:
0
{'prefix': 'foo', 'i': 1, 'foo1': 500}
Note that assigning attributes to objects is general - not only limited to scipy/numpy objects but applicable to all data types and custom objects (arrays, dataframes etc.). Of course that's a toy model - you can further develop it to make it able to be indexed, able to be pretty-printed, able to have elements removed, callable etc., based on your project needs. Just define the class at the beginning and then use it for storage-retrieval. That's the beauty of Python - it doesn't really have exactly what you seek especially if you come from MATLAB, but it can do so much more!

Related

Replacing Python's parser functionality?

First of all I want to mention that I know this is a horrible idea and it shouldn't be done. My intention is mainly curiosity and learning the innards of Python, and how to 'hack' them.
I was wondering whether it is at all possible to change what happens when we, for instance, use [] to create a list. Is there a way to modify how the parser behaves in order to, for instance, cause ["hello world"] to call print("hello world") instead of creating a list with one element?
I've attempted to find any documentation or posts about this but failed to do so.
Below is an example of replacing the built-in dict to instead use a custom class:
from __future__ import annotations
from typing import List, Any
import builtins
class Dict(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__dict__ = self
def subset(self, keys: List[Any]) -> Dict:
return Dict({key: self[key] for key in keys})
builtins.dict = Dict
When this module is imported, it replaces the dict built-in with the Dict class. However this only works when we directly call dict(). If we attempt to use {} it will fall back to the base dict built-in implementation:
import new_dict
a = dict({'a': 5, 'b': 8})
b = {'a': 5, 'b': 8}
print(type(a))
print(type(b))
Yields:
<class 'py_extensions.new_dict.Dict'>
<class 'dict'>
[] and {} are compiled to specific opcodes that specifically return a list or a dict, respectively. On the other hand list() and dict() compile to bytecodes that search global variables for list and dict and then call them as functions:
import dis
dis.dis(lambda:[])
dis.dis(lambda:{})
dis.dis(lambda:list())
dis.dis(lambda:dict())
returns (with some additional newlines for clarity):
3 0 BUILD_LIST 0
2 RETURN_VALUE
5 0 BUILD_MAP 0
2 RETURN_VALUE
7 0 LOAD_GLOBAL 0 (list)
2 CALL_FUNCTION 0
4 RETURN_VALUE
9 0 LOAD_GLOBAL 0 (dict)
2 CALL_FUNCTION 0
4 RETURN_VALUE
Thus you can overwrite what dict() returns simply by overwriting the global dict, but you can't overwrite what {} returns.
These opcodes are documented here. If the BUILD_MAP opcode runs, you get a dict, no way around it. As an example, here is the implementation of BUILD_MAP in CPython, which calls the function _PyDict_FromItems. It doesn't look at any kind of user-defined classes, it specifically makes a C struct that represents a python dict.
It is possible in at least some cases to manipulate the python bytecode at runtime. If you really wanted to make {} return a custom class, I suppose you could write some code to search for the BUILD_MAP opcode and replace it with the appropriate opcodes. Though those opcodes aren't the same size, so there's probably quite a few additional changes you'd have to make.
The ast module is an interface to Python's Abstract Syntax Tree which is built after parsing Python code.
It's possible to replace literal dict ({}) with dict call by modifying Abstract Syntax Tree of Python code.
import ast
import new_dict
a = dict({"a": 5, "b": 8})
b = {"a": 5, "b": 8}
print(type(a))
print(type(b))
print(type({"a": 5, "b": 8}))
src = """
a = dict({"a": 5, "b": 8})
b = {"a": 5, "b": 8}
print(type(a))
print(type(b))
print(type({"a": 5, "b": 8}))
"""
class RewriteDict(ast.NodeTransformer):
def visit_Dict(self, node):
# don't replace `dict({"a": 1})`
if isinstance(node.parent, ast.Call) and node.parent.func.id == "dict":
return node
# replace `{"a": 1} with `dict({"a": 1})
new_node = ast.Call(
func=ast.Name(id="dict", ctx=ast.Load()),
args=[node],
keywords=[],
type_comment=None,
)
return ast.fix_missing_locations(new_node)
tree = ast.parse(src)
# set parent to every node
for node in ast.walk(tree):
for child in ast.iter_child_nodes(node):
child.parent = node
RewriteDict().visit(tree)
exec(compile(tree, "ast", "exec"))
output;
<class 'new_dict.Dict'>
<class 'dict'>
<class 'dict'>
<class 'new_dict.Dict'>
<class 'new_dict.Dict'>
<class 'new_dict.Dict'>

How to call arbitrary deep python object?

I'm working on a script using an API from a software I use. I lack documentation for the methods available so I have little control over what I have. I'm looking to call a variable that can be buried arbitrarily deep in an object. I know the depth before hand but it can range widely.
Example:
index = ['foo','bar']
object['foo']['bar'].method()
I've tried something like:
temp = object[index[0]]
for ind in index[1:]:
temp = temp[ind]
temp.method()
But this makes a copy (it does not according to the replies I've gotten) of the object and does not apply the method correctly. Index can be arbitrarily long.
My only working solution is to hardcode this by using:
if lengthIndex == 1:
object[index[0]].method()
if lengthIndex == 2:
object[index[0]][index[1]].method()
if lengthIndex == 3:
object[index[0]][index[1]][index[2]].method()
# and so on ...
What is the proper way to code this?
Your first code sample doesn't copy the object. In Python, variables are references (at least that's how it effectively works), and when you assign something to a variable, you're just copying a memory address, not copying the thing itself. So if your only reason not to use that was that you wanted to avoid making unnecessary copies of objects, you have nothing to worry about.
What you do in your code sample is the way I would handle repeated indexing.
If the main point of the question is asking for a function to index arbitrarily nested dictionaries then see the following:
def f():
print('nested func')
def g():
print('nested func 2')
def nested_value(nested_dict, keys):
for k in keys:
nested_dict = nested_dict[k]
return nested_dict
nested_dict = {
'foo': {
'bar': {
'baz': f
},
'taco': g
}
}
keys = ['foo', 'bar', 'baz']
val = nested_value(nested_dict, keys)
val()
keys = ['foo', 'taco']
val = nested_value(nested_dict, keys)
val()
Output:
nested func
nested func 2
In [5]: def get_deep_object(obj, index):
...: for i in index:
...: obj = obj[i]
...: return obj
...:
In [6]: l = ["foo", "bar"]
In [7]: get_deep_object(l, [1])
Out[7]: 'bar'
In [8]: get_deep_object(l, [1,1])
Out[8]: 'a'
In [9]: get_deep_object(l, [1,1]).capitalize()
Out[9]: 'A'
You can get a shorter (but not necessarily more readable) solution via functional programming:
from operator import getitem
from functools import reduce
reduce(getitem, index, object).method()
getitem from operator is equivalent to lambda data, key: data[key].

PyYAML custom tag for tuple of tuples

I'm planning to use PyYAML for a configuration file. Some of the items
in that configuration file are Python tuples of tuples. So, I need a
convenient way to represent them. One can represent Python tuples of
tuples as follows using PyYAML
print yaml.load("!!python/tuple [ !!python/tuple [1, 2], !!python/tuple [3, 4]]")
However, this is not convenient notation for a long sequence of
items. I think it should be possible to define a custom tag, like
python/tuple_of_tuples. I.e. something like
yaml.load("!!python/tuple_of_tuples [[1,2], [3,4]]")
See my first attempt to define this below, by mimicking how
python/tuple is defined, and trying to do similar subclassing. It
fails, but gives an idea what I am after, I think. I have a second
attempt that works, but is a cheat, since it just calls eval.
If I can't find anything better I'll just use that. However, YAML is
intended as a replacement for ConfigObj, which uses INI files, and is
considerably less powerful than YAML, and I used the same approach
(namely eval) for tuples of tuples. So in that respect it will be no
worse.
A proper solution would be most welcome.
I have a couple of comments on my first solution.
I'd have thought that the constructor
construct_python_tuple_of_tuples would return the completed
structure, but in fact it seems to return an empty structure as
follows
([], [])
I traced the calls, and there seems to be a lot of complicated stuff
happening after construct_python_tuple_of_tuples is called.
The value that is returned is a tuple of lists of integers, so quite
close to the desired result. So, the structure must be completed
later.
The line with
tuple([tuple(t) for t in x])
was my attempt to coerce the list of tuples to a tuple of tuples, but
if I return that from construct_python_tuple_of_tuples, then the
resulting call to yaml.load("!!python/tuple_of_tuples [[1,2], [3,4]]") is just
((),())
Not sure what is with the
yaml.org,2002
Why 2002?
First attempt
import yaml
from yaml.constructor import Constructor
def construct_python_tuple_of_tuples(self, node):
# Complete content of construct_python_tuple
# is
# return tuple(self.construct_sequence(node))
print "node", node
x = tuple(self.construct_sequence(node))
print "x", x
foo = tuple([tuple(t) for t in x])
print "foo", foo
return x
Constructor.construct_python_tuple_of_tuples =
construct_python_tuple_of_tuples
Constructor.add_constructor(
u'tag:yaml.org,2002:python/tuple_of_tuples',
Constructor.construct_python_tuple_of_tuples)
y = yaml.load("!!python/tuple_of_tuples [[1,2], [3,4]]")
print "y", y, type(y)
print y[0], type(y[0])
print y[0][0], type(y[0][0])
The results are
node SequenceNode(tag=u'tag:yaml.org,2002:python/tuple_of_tuples',
value=[SequenceNode(tag=u'tag:yaml.org,2002:seq',
value=[ScalarNode(tag=u'tag:yaml.org,2002:int', value=u'1'),
ScalarNode(tag=u'tag:yaml.org,2002:int', value=u'2')]),
SequenceNode(tag=u'tag:yaml.org,2002:seq',
value=[ScalarNode(tag=u'tag:yaml.org,2002:int', value=u'3'),
ScalarNode(tag=u'tag:yaml.org,2002:int', value=u'4')])])
x ([], [])
foo ((), ())
y ([1, 2], [3, 4]) <type 'tuple'>
y[0] [1, 2] <type 'list'>
y[0][0] 1 <type 'int'>
Second attempt
import yaml
from yaml import YAMLObject, Loader, Dumper
class TupleOfTuples(YAMLObject):
yaml_loader = Loader
yaml_dumper = Dumper
yaml_tag = u'!TupleOfTuples'
#yaml_flow_style = ...
#classmethod
def from_yaml(cls, loader, node):
import ast
print "node", node
print "node.value", node.value, type(node.value)
return ast.literal_eval(node.value)
#classmethod
def to_yaml(cls, dumper, data):
return node
t = yaml.load("!TupleOfTuples ((1, 2), (3, 4))")
print "t", t, type(t)
The results are:
node ScalarNode(tag=u'!TupleOfTuples', value=u'((1, 2), (3, 4))')
node.value ((1, 2), (3, 4)) <type 'unicode'>
t ((1, 2), (3, 4)) <type 'tuple'>
To start with question 2 first: 2002 was the year this kind of tag was introduced in the Sep 1, 2002 version of the YAML 1.0 draft
Question 1 is more complicated. If you do:
from __future__ import print_function
import yaml
lol = [[1,2], [3,4]] # list of lists
print(yaml.dump(lol))
you get (A):
[[1, 2], [3, 4]]
But actually this is short for (B):
!!seq [
!!seq [
!!int "1",
!!int "2",
],
!!seq [
!!int "3",
!!int "4",
],
]
which is short for (C):
!<tag:yaml.org,2002:seq> [
!<tag:yaml.org,2002:seq> [
!<tag:yaml.org,2002:int> "1",
!<tag:yaml.org,2002:int> "2",
],
!<tag:yaml.org,2002:seq> [
!<tag:yaml.org,2002:int> "3",
!<tag:yaml.org,2002:int> "4",
],
]
A, B and C all load to the original list of list, because the seq(uence) is a built in type.
I don't think that extending the syntax of yaml (with e.g. () indicating a tuple would be a good idea. To minimize tags you reduce your example to:
yaml_in = "!tuple [ !tuple [1, 2], !tuple [3, 4]]"
and add a constructor:
yaml.add_constructor("!tuple", construct_tuple)
but this pushes the problem to creating the construct_tuple function. The one for a sequence (in constructor.py) is:
def construct_yaml_seq(self, node):
data = []
yield data
data.extend(self.construct_sequence(node))
But you cannot just replace the [] in there with () as changing the tuple by extending it will not work (the reason for this two step creation, with a yield, is e.g. to allow circular references in complex types like sequence and mapping).
You should define a Tuple() class that behaves like a list until "locked" (which you would do at the end of the contruction), and from then on it should behave like a tuple (i.e. no more modification). The following does so without subclassing yaml.YAMLObject, so you have to explicitly provide and register the constructor and representer for the Class.
class Tuple(list):
def _lock(self):
if hasattr(self, '_is_locked'):
return
self._is_locked = True
self.append = self._append
self.extend = self._extend
def _append(self, item):
raise AttributeError("'Tuple' object has no attribute 'append'")
def _extend(self, items):
raise AttributeError("'Tuple' object has no attribute 'extend'")
def __str__(self):
return '(' + ', '.join((str(e) for e in self)) + ')'
# new style class cannot assign something to special method
def __setitem__(self, key, value):
if getattr(self, '_is_locked', False):
raise TypeError("'Tuple' object does not support item assignment")
list.__setitem__(self, key, value)
def __delitem__(self, key, value):
if getattr(self, '_is_locked', False):
raise TypeError("'Tuple' object does not support item deletion")
list.__delitem__(self, key, value)
#staticmethod
def _construct_tuple(loader, data):
result = Tuple()
yield result
result.extend(loader.construct_sequence(data))
result._lock()
#staticmethod
def _represent_tuple(dumper, node):
return dumper.represent_sequence("!tuple", node)
# let yaml know how to handle this
yaml.add_constructor("!tuple", Tuple._construct_tuple)
yaml.add_representer(Tuple, Tuple._represent_tuple)
With that in place you can do:
yaml_in = "!tuple [ !tuple [1, 2], !tuple [3, 4]]"
#yaml_in = "!tuple [1, 2]"
data = yaml.load(yaml_in)
print(data)
print(data[1][0])
print(type(data))
to get:
((1, 2), (3, 4))
3
<class '__main__.Tuple'>
This is not a real tuple, but it doesn't allow list-like actions. The following activities all throw the appropriate error:
# test appending to the tuple,
try:
data.append(Tuple([5, 6]))
except AttributeError:
pass
else:
raise NotImplementedError
# test extending the tuple,
try:
data.extend([5, 6])
except AttributeError:
pass
else:
raise NotImplementedError
# test replacement of an item
try:
data[0] = Tuple([5, 6])
except TypeError:
pass
else:
raise NotImplementedError
# test deletion of an item
try:
del data[0]
except TypeError:
pass
else:
raise NotImplementedError
And finally you can do:
print(yaml.dump(data, default_flow_style=True))
for the following output:
!tuple [!tuple [1, 2], !tuple [3, 4]]
If you really want !tuple [[1, 2], [3, 4]] to create a Tuple of Tuples, you can do so by keeping context state in the the Baseloader class of yaml and overriding the method that construct python object from the sequences to Tuples or lists depending on context. That would probably have to be a stack of context states, to allow for nested use of !tuple as well as non-nested use, and some explicit overriding to get lists within tuples when using !!seq as tag.
I might have not checked Tuple() for completeness, and only implemented the restrictions that tuple has compared to list that immediately came to mind.
I tested this with my enhanced version of PyYAML: ruamel.yaml, but this should work the same in PyYAML itself.

Python higher-order sequence assignment?

Is there a way to group names together in python, to repeatedly assign to them en masse?
While we can do:
a,b,c = (1,2,3)
I would like to be able to do something like:
names = a,b,c
*names = (3,2,1) # this syntax doesn't work
a,b,c == (3,2,1) #=> True
Is there a built-in syntax for this? If not, I assume it would be possible with an object that overloads its assignment operator. In that case, is there an existing implementation, and would this concept have any unexpected failure modes?
The point is not to use the names as data, but rather to be able to use the actual names as variables that each refer to their own individual item, and to be able to use the list as a list, and to avoid code like:
a = 1
b = 2
c = 3
sequence = (a,b,c)
You should go one level up in your data abstraction. You are not trying to access the entries by their individual names -- you rather use names to denote the whole collection of values, so a simple list might be what you want.
If you want both, a name for the collection and names for the individual items, then a dictionary might be the way to go:
names = "a b c".split()
d = dict(zip(names, (1, 2, 3)))
d.update(zip(names, (3, 2, 1)))
If you need something like this repeatedly, you might want to define a class with the names as attributes:
class X(object):
def __init__(self, a, b, c):
self.update(a, b, c)
def update(self, a, b, c)
self.a, self.b, self.c = a, b, c
x = X(1, 2, 3)
x.update(3, 2, 1)
print x.a, x.b. x.c
This reflects that you want to block a, b and c to some common structure, but keep the option to access them individually by name.
This?
>>> from collections import namedtuple
>>> names = namedtuple( 'names', ['a','b','c'] )
>>> thing= names(3,2,1)
>>> thing.a
3
>>> thing.b
2
>>> thing.c
1
You should use a dict:
>>> d = {"a": 1, "b": 2, "c": 3}
>>> d.update({"a": 8})
>>> print(d)
{"a": 8, "c": 3, "b": 2}
I've realised that "exotic" syntax is probably unnecessary. Instead the following achieves what I wanted: (1) to avoid repeating the names and (2) to capture them as a sequence:
sequence = (a,b,c) = (1,2,3)
Of course, this won't allow:
*names = (3,2,1) # this syntax doesn't work
a,b,c == (3,2,1) #=> True
So, it won't facilitate repeated assignment to the same group of names without writing out those names repeatedly (except in a loop).
Well, you shouldn't do this, since it's potentially unsafe, but you can use the exec statement
>>> names = "a, b, c"
>>> tup = 1,2,3
>>> exec names + "=" + repr(tup)
>>> a, b, c
(1, 2, 3)
Python has such an elegant namespace system:
#!/usr/bin/env python
class GenericContainer(object):
def __init__(self, *args, **kwargs):
self._names = []
self._names.extend(args)
self.set(**kwargs)
def set(self, *args, **kwargs):
for i, value in enumerate(args):
self.__dict__[self._names[i]] = value
for name, value in kwargs.items():
if name not in self._names:
self._names.append(name)
self.__dict__[name] = value
def zip(self, names, values):
self.set(**dict(zip(names, values)))
def main():
x = GenericContainer('a', 'b', 'c')
x.set(1, 2, 3, d=4)
x.a = 10
print (x.a, x.b, x.c, x.d,)
y = GenericContainer(a=1, b=2, c=3)
y.set(3, 2, 1)
print (y.a, y.b, y.c,)
y.set(**dict(zip(('a', 'b', 'c'), (1, 2, 3))))
print (y.a, y.b, y.c,)
names = 'x', 'y', 'z'
y.zip(names, (4, 5, 6))
print (y.x, y.y, y.z,)
if __name__ == '__main__':
main()
Each instance of GenericContainer is an isolated namespace. IMHO it is better than messing with the local namespace even if you are programming under a pure procedural paradigm.
Not sure whether this is what you want...
>>> a,b,c = (1,2,3)
>>> names = (a,b,c)
>>> names
(1, 2, 3)
>>> (a,b,c) == names
True
>>> (a,b,c) == (1,2,3)
True

Is there a Python module/recipe (not numpy) for 2d arrays for small games

I am writing some small games in Python with Pygame & Pyglet as hobby projects.
A class for 2D array would be very handy. I use py2exe to send the games to relatives/friends and numpy is just too big and most of it's features are unnecessary for my requirements.
Could you suggest a Python module/recipe I could use for this.
-- Chirag
[Edit]:
List of lists would be usable as mentioned below by MatrixFrog and zvoase. But it is pretty primitive. A class with methods to insert/delete rows and columns as well as to rotate/flip the array would make it very easy and reusable too. dicts are good for sparse arrays only.
Thank you for your ideas.
How about using a defaultdict?
>>> import collections
>>> Matrix = lambda: collections.defaultdict(int)
>>> m = Matrix()
>>> m[3,2] = 6
>>> print m[3,4] # deliberate typo :-)
0
>>> m[3,2] += 4
>>> print m[3,2]
10
>>> print m
defaultdict(<type 'int'>, {(3, 2): 10, (3, 4): 0})
As the underlying dict uses tuples as keys, this supports 1D, 2D, 3D, ... matrices.
The simplest approach would just be to use nested lists:
>>> matrix = [[0] * num_cols] * num_rows
>>> matrix[i][j] = 'value' # row i, column j, value 'value'
>>> print repr(matrix[i][j])
'value'
Alternatively, if you’re going to be dealing with sparse matrices (i.e. matrices with a lot of empty or zero values), it might be more efficient to use nested dictionaries. In this case, you could implement setter and getter functions which will operate on a matrix, like so:
def get_element(mat, i, j, default=None):
# This will also set the accessed row to a dictionary.
row = mat.setdefault(i, {})
return row.setdefault(j, default)
def set_element(mat, i, j, value):
row = mat.setdefault(i, {})
row[j] = value
And then you would use them like this:
>>> matrix = {}
>>> set_element(matrix, 2, 3, 'value') # row 2, column 3, value 'value'
>>> print matrix
{2: {3: 'value'}}
>>> print repr(get_element(matrix, 2, 3))
'value'
If you wanted, you could implement a Matrix class which implemented these methods, but that might be overkill:
class Matrix(object):
def __init__(self, initmat=None, default=0):
if initmat is None: initmat = {}
self._mat = initmat
self._default = default
def __getitem__(self, pos):
i, j = pos
return self._mat.setdefault(i, {}).setdefault(j, self._default)
def __setitem__(self, pos, value):
i, j = pos
self._mat.setdefault(i, {})[j] = value
def __repr__(self):
return 'Matrix(%r, %r)' % (self._mat, self._default)
>>> m = Matrix()
>>> m[2,3] = 'value'
>>> print m[2,3]
'value'
>>> m
Matrix({2: {3: 'value'}}, 0)
Maybe pyeuclid matches your needs -- (dated but usable) formatted docs are here, up-to-date docs in ReST format are in this text file in the pyeuclid sources (to do your own formatting of ReST text, use the docutils).
I wrote the class. Don't know if it is a good or redundant but... Posted it here http://bitbucket.org/pieceofpeace/container2d/

Categories