Package: coordinates
coordinatesConvenience class for dealing with coordinates which need both maths and explicit ordering.MotivationNumpy arrays are great for doing maths with coordinates stored as arrays.Dicts are great for dealing with coordinate systems where the order keeps changing
(e.g. between C and Fortran order).But what if you want both?(Note: if you're doinglotsof maths... stick withnumpy)InstallationpipinstallcoordinatesUsageCoordinates areMappings (i.e.dict-like). They don't expose an interface for mutation, but
we're all consenting adults so if you really want to modify the internal_dict, I won't
stop you.InstantiationThey can be instantiated in any of the ways adictcan (from anotherMapping, a sequence of pairs,
some keyword arguments, or a mixture of the above).fromcoordinatesimportCoordinateCoordinate({'x':1,'y':2})Coordinate({'x':1},y=2)Coordinate([('x',1),('y',2)])Coordinate(x=1,y=2)If an order is defined (more on this later), you can also instantiate aCoordinatefrom a single
argument which is a sequence, or from a number of*args.Coordinate([1,2],order='xy')Coordinate(1,2,order='xy')Coordinate.default_order='xy'Coordinate([1,2])Coordinate(1,2)BecauseMappings can be instantiated from otherMappings, you can "extend" existing coordinates
into new dimensions.coord_2d=Coordinate(x=1,y=2)coord_3d=Coordinate(coord_2d,z=3)Finally, manyCoordinates can be instantiated lazily usingfrom_sequence:Coordinate.from_sequence([(1,2,3),(3,4,5)],order='xyz')Coordinate.from_sequence([{'x':1,'y':2},{'x':3,'y':4}],z=10)To note:order-dependent instantiation is incompatible with**kwargsInstantiation from a sequence of tuples will fail in 2D because it will be interpreted as
key-value pairs. Use a comprehension here instead:Coordinate.from_sequence(zip('xy', row) for row in sequence)MathsCoordinates do maths like you might expect them to, where the other operand is anything dict-like
with the same keys, or a number.coord=Coordinate(x=1,y=2,z=3)coord*2==Coordinate(x=2,y=4,z=6)>>>Truecoord**2==Coordinate(x=1,y=4,z=9)>>>Truecoord+coord==Coordinate(x=2,y=4,z=3)>>>Truecoord+=1# coord is a reference to a new object; no mutationcoord==Coordinate(x=2,y=3,z=4)>>>Trueabs(Coordinate(x=-10,y=10))==Coordinate(x=10,y=10)>>>Trueimportmathmath.ceil(Coordinate(x=0.5))==Coordinate(x=1)>>>Truemath.floor(Coordinate(x=0.5))==Coordinate(x=0)>>>TrueThey also have some convenience methods for getting the sum, product or norm of their keys.coord.sum()==9>>>Truecoord.prod()==24>>>TrueCoordinate(x=3,y=4).norm(order=2)==5>>>TrueOrderingYou can get the keys, values or items of theCoordinatein a specific order:coord.to_list('yxz')==[2,1,3]>>>Truelist(coord.items('yxz'))==[('y',2),('x',1),('z',3)]>>>TrueThe default order for a single instance can be given on instantiation, or mutated (this does not affect equality).The default order for allCoordinates can be set on the class. This affects existing instances, but does not
override their order if it was set explicitly.If neither an instanceorderor a classdefault_orderis set, it falls back to reverse lexicographic.coord3=Coordinate(x=1,y=2,z=3,order='zxy')coord3.order='yzx'Coordinate.default_order='xyz'SubclassingIf you're working in one space, thespaced_coordinatefactory can create custom subclasses with a fixed set of
keys and optionally a default order.fromcoordinatesimportspaced_coordinateCoordinateXYZC=spaced_coordinate('CoordinateXYZC','xyzc')# this will raise a ValueErrorCoordinateXYZC(x=1,y=2,z=3)Or you can subclassCoordinatedirectly.Value accessCoordinate values can be accessed with dict-like syntax (coord['x'],coord.get('y', 2)) or, for convenience,
attribute-like (coord.z) if the keys are strings.NoteIf you don't want the order-related functionality for another application, the base classMathDictis
implemented here too.