How to add axis names in dtwvis? - python

I am currently trying my hand at dynamic time warping. I am using the library dtaidistance for this. I currently only get the DTW graphs as a saved image. But I would like to add axis labels to them.
Does anyone know how I can add the axis labels? I already have the following code:
from dtaidistance import dtw
from dtaidistance import dtw_visualisation as dtwvis
[...]
s1 = array1
s2 = array2
d, paths = dtw.warping_paths(s1, s2)
best_path = dtw.best_path(paths)
dtwvis.plot_warpingpaths(s1, s2, paths, best_path,shownumbers=True)
path = dtw.warping_path(s1, s2)
dtwvis.plot_warping(s1, s2, path , filename="warp.svg")
distance = dtw.distance(s1, s2)
print("DTW distance=",distance)

Related

Best path in Dynamic Time Warping

I’ve been using different python DTW to find the shift between two images. Suppose we have image1 and image2 with the same shape. I apply dtw to each row of the two, i.e.
for i in range(image1.shape[0]):
alignment = dtw(image2[i,:], image1[I,:])
shift = alignment.index1 – alignment.index2
all_shifts.append(shift)
I need all_shifts to have the same shape as both images. However, in all the python dtw packages, the length of the alignment.index1 and index2 are larger than the length of s1 and s2, since there are duplicates. I have tried to select the unique indices by simple criteria. For example, in a synthetic test, I’ve selected those indices that have the maximum or minimum shifts, but the results are noisy. But in the real case, I don’t know the shifts between the two images in advance. So finding unique indices is not straightforward.
Could anyone advise me how I can find the best unique indices of the alignment, so that I could have a shift with the same length as s1 and s2?
Thank you,
I have tried the following code:
import numpy as np
import pandas as pd
from dtw import *
def warp_function(image2, image1):
warp_arr = np.zeros(image1.shape)
warped_img = np.zeros(image2.shape)
for i in range(image1.shape[0]):
print(f'[Warping function]: trace {i} out of {image1.shape[0]}')
reference = np.zeros(image1.shape[1])
query = np.zeros(image2.shape[1])
reference[:] = image1[i, :]
query[:] = image2[i, :]
alignment = dtw(query,reference)
shift = alignment.index2 - alignment.index1
warp_dict = {'shift': shift, 'index1': alignment.index1, 'index2': alignment.index2}
df = pd.DataFrame(warp_dict)
#df2 = df.drop_duplicates(subset=['index2'], keep='last')
df2 = find_uniques(df)
warp_arr[i,:] = df2['shift'].to_numpy()
warp_index = df2['index1'].to_numpy()
warped_img[i,:] = query[warp_index]
return warp_arr, warped_img
Find_unique is a function that finds minimum or maximum shifts for the duplicated indices.
I've been trying to find the shift between the two images using dtw. When I apply the shift, image2 and warped_img are expected to be the same. However, they are not. It shows that find_unique doesn't find the best unique indices.

Overlapping coefficient using scipy quad not working as expected

Trying to find the area overlap of two scipy.skewnorm distributions I have generated using the code below
import scipy
a1 =1
loc1 = 0
scale1 = 1
a2=2
loc2=0
scale2=1
print(scipy.integrate.quad(lambda x: (min(skewnorm.pdf(x, a1, loc=loc1, scale=scale1), skewnorm.pdf(x, a2, loc=loc2, scale=scale1))),-10,10))
output : (0.8975836176504333, 8.065277615563445e-10)
However, changing the limits, significantly affects my results.
print(scipy.integrate.quad(lambda x: (min(skewnorm.pdf(x, a1, loc=loc1, scale=scale1), skewnorm.pdf(x, a2, loc=loc2, scale=scale1))),-1,1))
output:(0.341344746068543, 3.789687964201238e-15)
How can I determine the limits to be used?

Scipy convolve2d with subsampling like Theano's conv2d?

I wish to perform 2D convolution on images of size 600 X 400 using a 10 X 10 filter. The filter is not separable. scipy.signal.convolve2d works well for me currently but, I am expecting a lot bigger images soon.
To counter that, I have two ideas
resizing images
subsampling (or striding)?
Focusing on the subsampling part, theano has a function which does convolution the same way as scipy convolve2d, see theano conv2d
It also has the subsampling option too. But, installing theano on windows has been painful to me. How do I get subsampling work with scipy.signal.convolve2d? Any other alternatives (which doesn't require me installing me some heavyweight library)?
You could implement subsampling by hand, I'll only sketch 1d for simplicity. Say you want to sample s = d * f on a regular subgrid with spacing k. Then your nth sample is s_nk = sum_i=0^10 f_i d_nk-i. The thing to observe here is that the indices of f and d always sum to a multiple of k. This suggests splitting it up into sub-sums s_nk = sum_j=0^k-1 sum_i=0^10/k f_j+ik d_-j+(n-i)k. So what you need to do is: subsample d and f at grids with spacing k at all offsets 0, ..., k-1. Convolve all pairs of subsampled d and f whose offsets sum to 0 or k and add the results.
Here's some code for 1d. It roughly implements the above, only the grids are placed slightly differently to make index management easier. The second function does it the stupid way, i.e. computes the full convolution and then decimates. It is for testing the first function against.
import numpy as np
from scipy import signal
def ss_conv(d1, d2, decimate):
n = (len(d1) + len(d2) - 1) // decimate
out = np.zeros((n,))
for i in range(decimate):
d1d = d1[i::decimate]
d2d = d2[decimate-i-1::decimate]
cv = signal.convolve(d1d, d2d, 'full')
out[:len(cv)] += cv
return out
def conv_ss(d1, d2, decimate):
return signal.convolve(d1, d2, 'full')[decimate-1::decimate]
Edit: 2d version:
import numpy as np
from scipy import signal
def ss_conv_2d(d1, d2, decy, decx):
ny = (d1.shape[0] + d2.shape[0] - 1) // decy
nx = (d1.shape[1] + d2.shape[1] - 1) // decx
out = np.zeros((ny, nx))
for i in range(decy):
for j in range(decx):
d1d = d1[i::decy, j::decx]
d2d = d2[decy-i-1::decy, decx-j-1::decx]
cv = signal.convolve2d(d1d, d2d, 'full')
out[:cv.shape[0], :cv.shape[1]] += cv
return out
def conv_ss_2d(d1, d2, decy, decx):
return signal.convolve2d(d1, d2, 'full')[decy-1::decy, decx-1::decx]

How to reshape a networkx graph in Python?

So I created a really naive (probably inefficient) way of generating hasse diagrams.
Question:
I have 4 dimensions... p q r s .
I want to display it uniformly (tesseract) but I have no idea how to reshape it. How can one reshape a networkx graph in Python?
I've seen some examples of people using spring_layout() and draw_circular() but it doesn't shape in the way I'm looking for because they aren't uniform.
Is there a way to reshape my graph and make it uniform? (i.e. reshape my hasse diagram into a tesseract shape (preferably using nx.draw() )
Here's what mine currently look like:
Here's my code to generate the hasse diagram of N dimensions
#!/usr/bin/python
import networkx as nx
import matplotlib.pyplot as plt
import itertools
H = nx.DiGraph()
axis_labels = ['p','q','r','s']
D_len_node = {}
#Iterate through axis labels
for i in xrange(0,len(axis_labels)+1):
#Create edge from empty set
if i == 0:
for ax in axis_labels:
H.add_edge('O',ax)
else:
#Create all non-overlapping combinations
combinations = [c for c in itertools.combinations(axis_labels,i)]
D_len_node[i] = combinations
#Create edge from len(i-1) to len(i) #eg. pq >>> pqr, pq >>> pqs
if i > 1:
for node in D_len_node[i]:
for p_node in D_len_node[i-1]:
#if set.intersection(set(p_node),set(node)): Oops
if all(p in node for p in p_node) == True: #should be this!
H.add_edge(''.join(p_node),''.join(node))
#Show Plot
nx.draw(H,with_labels = True,node_shape = 'o')
plt.show()
I want to reshape it like this:
If anyone knows of an easier way to make Hasse Diagrams, please share some wisdom but that's not the main aim of this post.
This is a pragmatic, rather than purely mathematical answer.
I think you have two issues - one with layout, the other with your network.
1. Network
You have too many edges in your network for it to represent the unit tesseract. Caveat I'm not an expert on the maths here - just came to this from the plotting angle (matplotlib tag). Please explain if I'm wrong.
Your desired projection and, for instance, the wolfram mathworld page for a Hasse diagram for n=4 has only 4 edges connected all nodes, whereas you have 6 edges to the 2 and 7 edges to the 3 bit nodes. Your graph fully connects each "level", i.e. 4-D vectors with 0 1 values connect to all vectors with 1 1 value, which then connect to all vectors with 2 1 values and so on. This is most obvious in the projection based on the Wikipedia answer (2nd image below)
2. Projection
I couldn't find a pre-written algorithm or library to automatically project the 4D tesseract onto a 2D plane, but I did find a couple of examples, e.g. Wikipedia. From this, you can work out a co-ordinate set that would suit you and pass that into the nx.draw() call.
Here is an example - I've included two co-ordinate sets, one that looks like the projection you show above, one that matches this one from wikipedia.
import networkx as nx
import matplotlib.pyplot as plt
import itertools
H = nx.DiGraph()
axis_labels = ['p','q','r','s']
D_len_node = {}
#Iterate through axis labels
for i in xrange(0,len(axis_labels)+1):
#Create edge from empty set
if i == 0:
for ax in axis_labels:
H.add_edge('O',ax)
else:
#Create all non-overlapping combinations
combinations = [c for c in itertools.combinations(axis_labels,i)]
D_len_node[i] = combinations
#Create edge from len(i-1) to len(i) #eg. pq >>> pqr, pq >>> pqs
if i > 1:
for node in D_len_node[i]:
for p_node in D_len_node[i-1]:
if set.intersection(set(p_node),set(node)):
H.add_edge(''.join(p_node),''.join(node))
#This is manual two options to project tesseract onto 2D plane
# - many projections are available!!
wikipedia_projection_coords = [(0.5,0),(0.85,0.25),(0.625,0.25),(0.375,0.25),
(0.15,0.25),(1,0.5),(0.8,0.5),(0.6,0.5),
(0.4,0.5),(0.2,0.5),(0,0.5),(0.85,0.75),
(0.625,0.75),(0.375,0.75),(0.15,0.75),(0.5,1)]
#Build the "two cubes" type example projection co-ordinates
half_coords = [(0,0.15),(0,0.6),(0.3,0.15),(0.15,0),
(0.55,0.6),(0.3,0.6),(0.15,0.4),(0.55,1)]
#make the coords symmetric
example_projection_coords = half_coords + [(1-x,1-y) for (x,y) in half_coords][::-1]
print example_projection_coords
def powerset(s):
ch = itertools.chain.from_iterable(itertools.combinations(s, r) for r in range(len(s)+1))
return [''.join(t) for t in ch]
pos={}
for i,label in enumerate(powerset(axis_labels)):
if label == '':
label = 'O'
pos[label]= example_projection_coords[i]
#Show Plot
nx.draw(H,pos,with_labels = True,node_shape = 'o')
plt.show()
Note - unless you change what I've mentioned in 1. above, they still have your edge structure, so won't look exactly the same as the examples from the web. Here is what it looks like with your existing network generation code - you can see the extra edges if you compare it to your example (e.g. I don't this pr should be connected to pqs:
'Two cube' projection
Wikimedia example projection
Note
If you want to get into the maths of doing your own projections (and building up pos mathematically), you might look at this research paper.
EDIT:
Curiosity got the better of me and I had to search for a mathematical way to do this. I found this blog - the main result of which being the projection matrix:
This led me to develop this function for projecting each label, taking the label containing 'p' to mean the point has value 1 on the 'p' axis, i.e. we are dealing with the unit tesseract. Thus:
def construct_projection(label):
r1 = r2 = 0.5
theta = math.pi / 6
phi = math.pi / 3
x = int( 'p' in label) + r1 * math.cos(theta) * int('r' in label) - r2 * math.cos(phi) * int('s' in label)
y = int( 'q' in label) + r1 * math.sin(theta) * int('r' in label) + r2 * math.sin(phi) * int('s' in label)
return (x,y)
Gives a nice projection into a regular 2D octagon with all points distinct.
This will run in the above program, just replace
pos[label] = example_projection_coords[i]
with
pos[label] = construct_projection(label)
This gives the result:
play with r1,r2,theta and phi to your heart's content :)

How to visualize a hidden Markov model in Python?

I think the question is clear enough. I want to make a hidden Markov model in Python and draw a vizualization model of it. So, it's something like this picture:
Is there any module to do that? I've googled it and found nothing.
The dot package from graphviz is the best I've found. The syntax is simple, simpler than xml.
Though I've never worked with Hidden Markov Models, when I need to visualize a graph (directed, with labels, colors, etc.), I use Gephi, a GUI graph browser/editor and generate the graphs programmatically as GraphML files, which is an XML-based format. Python has good XML-handling tools (in the standard library and lxml). Gephi recognizes some of the <data> sub-elements as positions, colors, and labels for nodes and edges.
The Python library pomegranate has good support for Hidden Markov Models. It includes functionality for defining such models, learning it from data, doing inference, and visualizing the transitions graph (as you request here).
Below is example code for defining a model, and plotting the states and transitions. The image output will be like this:
from pomegranate import HiddenMarkovModel, State, DiscreteDistribution
from matplotlib import pyplot as plt
def build_model():
d1 = DiscreteDistribution({'A' : 0.50, 'B' : 0.50})
d2 = DiscreteDistribution({'A' : 0.10, 'B' : 0.90})
d3 = DiscreteDistribution({'A' : 0.90, 'B' : 0.10})
s1 = State(d1, name="s1")
s2 = State(d2, name="s2")
s3 = State(d3, name="s3")
model = HiddenMarkovModel(name='my model')
model.add_states(s1, s2, s3)
model.add_transition(model.start, s1, 1.0)
model.add_transition(s1, s1, 0.7)
model.add_transition(s1, s2, 0.3) # s1->s2
model.add_transition(s2, s2, 0.8)
model.add_transition(s2, s3, 0.0) # no transition from s2 to s3
model.add_transition(s1, s3, 0.1) # indirect from s1 to s3
model.add_transition(s3, s1, 0.1) # indirect from s3 to s1
model.add_transition(s3, s3, 0.9)
model.add_transition(s3, model.end, 0.1)
model.start.name = 'start'
model.end.name = 'end'
model.bake()
return model
model = build_model()
fig, ax = plt.subplots(1)
model.plot(ax=ax, precision=2)
fig.savefig('model.png')

Categories