Note
Go to the end to download the full example code
Usage Examples
This shows some common use cases of the TMap
class.
Typical Use Cases
We use a Numpy array as underlying storage because it allows for slicing and advanced indexing, which is used to provide some of the functionality.
import numpy as np
from triangularmap import TMap
n = 4
arr = np.array([" "] * TMap.size_from_n(n))
tmap = TMap(arr)
Of course, we can set single elements:
tmap[1, 4] = 'o'
print(tmap.pretty(haxis=True))
╱╲
╱ ╲
╱╲ ╱╲
╱ ╲╱ o╲
╱╲ ╱╲ ╱╲
╱ ╲╱ ╲╱ ╲
╱╲ ╱╲ ╱╲ ╱╲
╱ ╲╱ ╲╱ ╲╱ ╲
│ │ │ │ │
0 1 2 3 4
Slicing
Horizontal Slices
We can also set entire rows, specified by depth
tmap.dslice[2] = 'o'
print(tmap.pretty(daxis=True))
╱╲ depth
╱ ╲ 0
╱╲ ╱╲
╱ ╲╱ o╲ 1
╱╲ ╱╲ ╱╲
╱ o╲╱ o╲╱ o╲ 2
╱╲ ╱╲ ╱╲ ╱╲
╱ ╲╱ ╲╱ ╲╱ ╲ 3
or level
tmap.lslice[3] = 'x'
print(tmap.pretty(laxis=True))
╱╲ level
╱ ╲ 4
╱╲ ╱╲
╱ x╲╱ x╲ 3
╱╲ ╱╲ ╱╲
╱ o╲╱ o╲╱ o╲ 2
╱╲ ╱╲ ╱╲ ╱╲
╱ ╲╱ ╲╱ ╲╱ ╲ 1
This syntax is required because the dslice()
and
lslice()
methods return a (sliced) view of the underlying numpy array, which allows
for directly assigning values. We can also slice the returned array again
tmap.lslice[1][1:3] = 'x'
print(tmap.pretty(laxis=True))
╱╲ level
╱ ╲ 4
╱╲ ╱╲
╱ x╲╱ x╲ 3
╱╲ ╱╲ ╱╲
╱ o╲╱ o╲╱ o╲ 2
╱╲ ╱╲ ╱╲ ╱╲
╱ ╲╱ x╲╱ x╲╱ ╲ 1
Let’s reset the underlying array
tmap.arr[:] = " "
print(tmap.pretty())
╱╲
╱ ╲
╱╲ ╱╲
╱ ╲╱ ╲
╱╲ ╱╲ ╱╲
╱ ╲╱ ╲╱ ╲
╱╲ ╱╲ ╱╲ ╱╲
╱ ╲╱ ╲╱ ╲╱ ╲
Vertical Slices
We can also use vertical slices defined by a start index
tmap.sslice[1] = 'o'
print(tmap.pretty(haxis=True))
╱╲
╱ ╲
╱╲ ╱╲
╱ ╲╱ o╲
╱╲ ╱╲ ╱╲
╱ ╲╱ o╲╱ ╲
╱╲ ╱╲ ╱╲ ╱╲
╱ ╲╱ o╲╱ ╲╱ ╲
│ │ │ │ │
0 1 2 3 4
or an end index
tmap.eslice[3] = 'x'
print(tmap.pretty(haxis=True))
╱╲
╱ ╲
╱╲ ╱╲
╱ x╲╱ o╲
╱╲ ╱╲ ╱╲
╱ ╲╱ x╲╱ ╲
╱╲ ╱╲ ╱╲ ╱╲
╱ ╲╱ o╲╱ x╲╱ ╲
│ │ │ │ │
0 1 2 3 4
In contrast to the dslice()
and lslice()
method, we
cannot directly slice the underlying array, because it is not aligned with slicing direction. Instead, behind the
scenes, advanced indexing is used to get and set the elements. For simple getting or setting, this does not make
a big difference. But it can lead to subtle bugs when first getting a slice and then trying to set elements, because
the sslice
and eslice
attributes effectively
correspond to copies not views of the underlying array. To demonstrate this, let’s first fill the map with numbers
tmap.arr[:] = list(range(len(tmap.arr))) # implicitly converted to strings by Numpy
print(tmap.pretty(haxis=True, laxis=True))
╱╲ level
╱ 0╲ 4
╱╲ ╱╲
╱ 1╲╱ 2╲ 3
╱╲ ╱╲ ╱╲
╱ 3╲╱ 4╲╱ 5╲ 2
╱╲ ╱╲ ╱╲ ╱╲
╱ 6╲╱ 7╲╱ 8╲╱ 9╲ 1
│ │ │ │ │
0 1 2 3 4
When we now do the following
lslice = tmap.lslice[2]
sslice = tmap.sslice[1]
print(lslice)
print(sslice)
['3' '4' '5']
['7' '4' '2']
we get the corresponding slices, as expected. However, when we try to set elements via these objects
lslice[:] = "X"
sslice[:] = "O"
print(lslice)
print(sslice)
print(tmap.pretty(haxis=True, laxis=True))
['X' 'X' 'X']
['O' 'O' 'O']
╱╲ level
╱ 0╲ 4
╱╲ ╱╲
╱ 1╲╱ 2╲ 3
╱╲ ╱╲ ╱╲
╱ X╲╱ X╲╱ X╲ 2
╱╲ ╱╲ ╱╲ ╱╲
╱ 6╲╱ 7╲╱ 8╲╱ 9╲ 1
│ │ │ │ │
0 1 2 3 4
we see that the original map is only affected for the lslice object, because it is a view, while the sslice object is a copy of the underlying storage (likewise for dslice and eslice).
Total running time of the script: (0 minutes 0.005 seconds)