Set numeric interval limits as input to plotting function in Matplotlib - python

I am plotting this figure but I would like to play with the intervals.
However, I don't want to have to modify the legend, DataFrame column names and other variables every single time manually. ideally I would send the ranges "<", "<=", ">=" as input arguments. Is this possible in Python?
The code:
def plotHistDistances(pat_name, lesion_id, rootdir, distanceMap, num_voxels, title, ablation_date):
# PLOT THE HISTOGRAM FOR THE MAUERER EUCLIDIAN DISTANCES
lesion_id_str = str(lesion_id)
lesion_id = lesion_id_str.split('.')[0]
figName_hist = 'Pat_' + str(pat_name) + '_Lesion' + str(lesion_id) + '_AblationDate_' + ablation_date + '_histogram'
min_val = int(np.floor(min(distanceMap)))
max_val = int(np.ceil(max(distanceMap)))
fig, ax = plt.subplots(figsize=(18, 16))
col_height, bins, patches = ax.hist(distanceMap, ec='darkgrey', bins=range(min_val - 1, max_val + 1))
voxels_nonablated = []
voxels_insuffablated = []
voxels_ablated = []
for b, p, col_val in zip(bins, patches, col_height):
if b < 0:
voxels_nonablated.append(col_val)
elif 0 <= b <= 5:
voxels_insuffablated.append(col_val)
elif b > 5:
voxels_ablated.append(col_val)
# %%
'''calculate the total percentage of surface for ablated, non-ablated, insufficiently ablated'''
voxels_nonablated = np.asarray(voxels_nonablated)
voxels_insuffablated = np.asarray(voxels_insuffablated)
voxels_ablated = np.asarray(voxels_ablated)
sum_perc_nonablated = ((voxels_nonablated / num_voxels) * 100).sum()
sum_perc_insuffablated = ((voxels_insuffablated / num_voxels) * 100).sum()
sum_perc_ablated = ((voxels_ablated / num_voxels) * 100).sum()
# %%
'''iterate through the bins to change the colors of the patches bases on the range [mm]'''
for b, p, col_val in zip(bins, patches, col_height):
if b < 0:
plt.setp(p, label='Ablation Surface Margin ' + r'$x < 0$' + 'mm :' + " %.2f" % sum_perc_nonablated + '%')
elif 0 <= b <= 5:
plt.setp(p, 'facecolor', 'orange',
label='Ablation Surface Margin ' + r'$0 \leq x \leq 5$' + 'mm: ' + "%.2f" % sum_perc_insuffablated + '%')
elif b > 5:
plt.setp(p, 'facecolor', 'darkgreen',
label='Ablation Surface Margin ' + r'$x > 5$' + 'mm: ' + " %.2f" % sum_perc_ablated + '%')
# %%
'''edit the axes limits and labels'''
plt.xlabel('Euclidean Distances [mm]', fontsize=30, color='black')
plt.tick_params(labelsize=28, color='black')
ax.tick_params(colors='black', labelsize=28)
plt.grid(True)
# TODO: set equal axis limits
ax.set_xlim([-15, 15])
# edit the y-ticks: change to percentage of surface
yticks, locs = plt.yticks()
percent = (yticks / num_voxels) * 100
percentage_surface_rounded = np.round(percent)
yticks_percent = [str(x) + '%' for x in percentage_surface_rounded]
new_yticks = (percentage_surface_rounded * yticks) / percent
new_yticks[0] = 0
plt.yticks(new_yticks, yticks_percent)
# plt.yticks(yticks,yticks_percent)
plt.ylabel('Percentage of tumor surface voxels', fontsize=30, color='black')
handles, labels = plt.gca().get_legend_handles_labels()
by_label = OrderedDict(zip(labels, handles))
plt.legend(by_label.values(), by_label.keys(), fontsize=30, loc='best')
plt.title(title + '. Patient ' + str(pat_name) + '. Lesion ' + str(lesion_id), fontsize=30)
The figure:
So I would like to send the intervals you see in legend as input here:
def plotHistDistances(pat_name, lesion_id, rootdir, distanceMap,
num_voxels, title, ablation_date, interval_limits):

The idea is to parameterize the range element (i.e. 0 and 5 in your sample code) into interval_limits. To do so I have assumed that the parameter interval_limits will be a list of 2 values in the following form: [min_value, max_value] or concretely given your sample, interval_limits should be a list of 0, 5 like the following:
interval_limits = [0, 5]
Based on the assumption, I have modified your code a little bit. Pay attention to the new block where I assign the first element of interval_limits into a new variable min_limit and the 2nd element of interval_limits into another new variable max_limit and then I have modified the label string using the '%.2f format (feel free to change into whatever format you want)
Here's the code:
def plotHistDistances(pat_name, lesion_id, rootdir, distanceMap, num_voxels, title, ablation_date, interval_limits):
##########################################
# NEW COODE SECTION
##########################################
# Check if interval_limits contains all the limits
if len(interval_limits) != 2:
raise ValueError("2 limits are expected, got {} instead.".format(len(interval_limits)))
# Assign the limits
min_limit = interval_limits[0]
max_limit = interval_limits[1]
##########################################
# END OF NEW CODE SECTION
##########################################
# PLOT THE HISTOGRAM FOR THE MAUERER EUCLIDIAN DISTANCES
lesion_id_str = str(lesion_id)
lesion_id = lesion_id_str.split('.')[0]
figName_hist = 'Pat_' + str(pat_name) + '_Lesion' + str(lesion_id) + '_AblationDate_' + ablation_date + '_histogram'
min_val = int(np.floor(min(distanceMap)))
max_val = int(np.ceil(max(distanceMap)))
fig, ax = plt.subplots(figsize=(18, 16))
col_height, bins, patches = ax.hist(distanceMap, ec='darkgrey', bins=range(min_val - 1, max_val + 1))
voxels_nonablated = []
voxels_insuffablated = []
voxels_ablated = []
for b, p, col_val in zip(bins, patches, col_height):
if b < min_limit:
voxels_nonablated.append(col_val)
elif min_limit <= b <= max_limit:
voxels_insuffablated.append(col_val)
elif b > max_limit:
voxels_ablated.append(col_val)
# %%
'''calculate the total percentage of surface for ablated, non-ablated, insufficiently ablated'''
voxels_nonablated = np.asarray(voxels_nonablated)
voxels_insuffablated = np.asarray(voxels_insuffablated)
voxels_ablated = np.asarray(voxels_ablated)
sum_perc_nonablated = ((voxels_nonablated / num_voxels) * 100).sum()
sum_perc_insuffablated = ((voxels_insuffablated / num_voxels) * 100).sum()
sum_perc_ablated = ((voxels_ablated / num_voxels) * 100).sum()
# %%
'''iterate through the bins to change the colors of the patches bases on the range [mm]'''
for b, p, col_val in zip(bins, patches, col_height):
if b < min_limit:
plt.setp(p, label='Ablation Surface Margin ' + r'$x < %.2f$' % min_limit + 'mm :' + " %.2f" % sum_perc_nonablated + '%')
elif min_limit <= b <= max_limit:
plt.setp(p, 'facecolor', 'orange',
label='Ablation Surface Margin ' + r'$%.2f \leq x \leq %.2f$' % (min_limit, max_limit) + 'mm: ' + "%.2f" % sum_perc_insuffablated + '%')
elif b > max_limit:
plt.setp(p, 'facecolor', 'darkgreen',
label='Ablation Surface Margin ' + r'$x > %.2f$' % max_limit + 'mm: ' + " %.2f" % sum_perc_ablated + '%')
# %%
'''edit the axes limits and labels'''
plt.xlabel('Euclidean Distances [mm]', fontsize=30, color='black')
plt.tick_params(labelsize=28, color='black')
ax.tick_params(colors='black', labelsize=28)
plt.grid(True)
# TODO: set equal axis limits
ax.set_xlim([-15, 15])
# edit the y-ticks: change to percentage of surface
yticks, locs = plt.yticks()
percent = (yticks / num_voxels) * 100
percentage_surface_rounded = np.round(percent)
yticks_percent = [str(x) + '%' for x in percentage_surface_rounded]
new_yticks = (percentage_surface_rounded * yticks) / percent
new_yticks[0] = 0
plt.yticks(new_yticks, yticks_percent)
# plt.yticks(yticks,yticks_percent)
plt.ylabel('Percentage of tumor surface voxels', fontsize=30, color='black')
handles, labels = plt.gca().get_legend_handles_labels()
by_label = OrderedDict(zip(labels, handles))
plt.legend(by_label.values(), by_label.keys(), fontsize=30, loc='best')
plt.title(title + '. Patient ' + str(pat_name) + '. Lesion ' + str(lesion_id), fontsize=30)
Disclaimer: I have not tested this code since I do not have the full set of parameter to reproduce the result, but that should work. If it doesn't feel free to provide me the set of parameters you use and I will see how I can rectify the issue

Related

Is there a way I can plot a figure at initial and final conditions?

The code I am using works and is written correctly, only I want to have plots of the initial and final conditions (time = 0, time = .01)
Whenever run the code to show plot at n=0,10 I get the error "show() got an unexpected keyword argument 'n'."
import numpy
import matplotlib.pyplot as plt
n = 10 #number of timesteps
dt = .001 #(timestep)
L = 1.0 #domain (total length)
dx = 0.1 #spacial resolution
T0 = float(q + 1)
T1s = float(q + 1 - r)
T2s = float(q + 1 + s)
t_final = n*dt
alpha = float(p + 1)
x = np.linspace(0, L, n)
T = np.ones(n)*T0
dTdt = np.empty(n)
t = np.arange(0,t_final, dt)
for j in range(1,len(t)):
plt.clf()
for i in range(1,n-1):
dTdt[i] = alpha*(-(T[i]-T[i-1])/dx**2+(T[i+1]-T[i])/dx**2)
dTdt[0] = alpha*(-(T[0]-T1s)/dx**2+(T[1]-T[0])/dx**2)
dTdt[n-1] = alpha*(-(T[n-1]-T[n-2])/dx**2+(T2s-T[n-1])/dx**2)
T = T + dTdt*dt
plt.figure(1)
plt.plot(x,T)
plt.axis([0, L, 0, 14])
plt.xlabel('Distance')
plt.ylabel('Temperature')
plt.show(n=0)
plt.show(n=10)
Of course, because matplotlib doesn't know what "n" is. I suspect what you want is to replace the last seven lines with:
if j == 0 or j == n-1:
plt.figure(1)
plt.plot(x,T)
plt.axis([0, L, 0, 14])
plt.xlabel('Distance')
plt.ylabel('Temperature')
plt.show()

Barabasi-Albert model, wrong degree exponent

I'm trying to generate a scale-free network using the Barabasi-Albert model. The model predicts a degree distribution that follows p(k) ~ k^-3 but mine shows k^-2.
The algorithm was taken from Barabasi's book at this URL: http://barabasi.com/networksciencebook,
here is the relevant paragraph:
Barabasi's algorithm
Here is my code, could someone please help me figure out what is wrong?
import numpy as np
import matplotlib.pyplot as plt
from collections import Counter
plt.rcParams["figure.figsize"] = (15,6)
#initialize values
N = 10000
k = 2
m = int(k / 2)
#initialize matrices
adjacency = np.zeros((N,N))
degrees = np.zeros(N)
#add links
for i in range(N):
degrees[i] = m
for c in range(m):
# choose a node with probability proportional to it's degree
j = np.random.choice(N, p = degrees / (2 * m * i + m + c))
degrees[j] += 1
adjacency[i][j] += 1
adjacency[j][i] += 1
def get_binned_data(labels, values, num):
min_label, max_label = min(labels), max(labels)
base = (max_label / min_label) ** (1 / num)
bins = [base**i for i in range(int(np.log(max_label) / np.log(base)) + 1)]
binned_values, binned_labels = [], []
counter = 0
for b in bins:
bin_size = 0
bin_sum = 0
while counter < len(labels) and labels[counter] <= b:
bin_size += values[counter]
bin_sum += values[counter] * labels[counter]
counter += 1
if(bin_size):
binned_values.append(bin_size)
binned_labels.append(bin_sum / bin_size)
return binned_labels, binned_values
labels, values = zip(*sorted(Counter(degrees).items(), key = lambda pair:
pair[0]))
binned_labels, binned_values = get_binned_data(labels, values, 15)
fig, (ax1, ax2) = plt.subplots(ncols = 2, nrows = 1)
fig.suptitle('Barabasi-Albert Model',fontsize = 25)
ax1.loglog(binned_labels, binned_values, basex = 10, basey = 10, linestyle =
'None', marker = 'o', color = 'red')
ax1.set(xlabel = 'degree', ylabel = '# of nodes')
ax1.set_title('log-log scale (log-binned)',{'fontsize':'15'})
ax2.plot(labels, values, 'ro')
ax2.set(xlabel = 'degree', ylabel = '# of nodes')
ax2.set_title('linear scale',{'fontsize':'15'})
plt.show()
Your code does not run (probabilities in np.random.choice do not sum to 1). Why not p = degrees/np.sum(degrees)?
According to Wikipedia, you need to start with some already connected nodes, whereas you start from nothing. Also, you should probably put degrees[i] = m after the inner loop to avoid forming links from node i to itself.
This might help, but it's not clear to me how you generate your degree plot, so I can't verify it.

Why is this Linear Classifier algorithm wrong?

I specify 'n' amount of points. Label them +1 or -1. I store all this in a dictionary that looks like: {'point1' : [(0.565,-0.676), +1], ... }. I am trying to find a line that separates them - i.e. points labeled +1 above the line, those -1 below the line. Can anyone help?
I'm trying to apply w = w + y(r) as the "learning algorithm", w is the weight vector y is +1 or -1, r is the point
The code runs but the separating line is not precise - it doesn't separate correctly. Also, as I increase the number of points to separate, the line gets less efficient.
If you run the code, the green line is supposed to be the separating line. The closer it get to the slope of the blue line (the perfect line by definition), the better.
from matplotlib import pyplot as plt
import numpy as np
import random
n = 4
x_values = [round(random.uniform(-1,1),3) for _ in range(n)]
y_values = [round(random.uniform(-1,1),3) for _ in range(n)]
pts10 = zip(x_values, y_values)
label_dict = {}
x1, y1, x2, y2 = (round(random.uniform(-1,1),3) for _ in range(4))
b = [x1, y1]
d = [x2, y2]
slope, intercept = np.polyfit(b, d, 1)
fig, ax = plt.subplots(figsize=(8,8))
ax.scatter(*zip(*pts10), color = 'black')
ax.plot(b,d,'b-')
label_plus = '+'
label_minus = '--'
i = 1
for x,y in pts10:
if(y > (slope*x + intercept)):
ax.annotate(label_plus, xy=(x,y), xytext=(0, -10), textcoords='offset points', color = 'blue', ha='center', va='center')
label_dict['point{}'.format(i)] = [(x,y), "+1"]
else:
ax.annotate(label_minus, xy=(x,y), xytext=(0, -10), textcoords='offset points', color = 'red', ha='center', va='center')
label_dict['point{}'.format(i)] = [(x,y), "-1"]
i += 1
# this is the algorithm
def check(ww,rr):
while(np.dot(ww,rr) >= 0):
print "being refined 1"
ww = np.subtract(ww,rr)
return ww
def check_two(ww,rr):
while(np.dot(ww,rr) < 0):
print "being refined 2"
ww = np.add(ww,rr)
return ww
w = np.array([0,0])
ii = 1
for x,y in pts10:
r = np.array([x,y])
print w
if (np.dot(w,r) >= 0) != int(label_dict['point{}'.format(ii)][1]) < 0:
print "Point " + str(ii) + " should have been below the line"
w = np.subtract(w,r)
w = check(w,r)
elif (np.dot(w,r) < 0) != int(label_dict['point{}'.format(ii)][1]) >= 0:
print "Point " + str(ii) + " should have been above the line"
w = np.add(w,r)
w = check_two(w,r)
else:
print "Point " + str(ii) + " is in the correct position"
ii += 1
ax.plot(w,'g--')
ax.set_xlabel('X-axis')
ax.set_ylabel('Y-axis')
ax.set_title('Labelling 10 points')
ax.set_xticks(np.arange(-1, 1.1, 0.2))
ax.set_yticks(np.arange(-1, 1.1, 0.2))
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.legend()
You can for example use the SGDClassifier from scikit-learn (sklearn). The linear classifiers compute predictions as follows (see the source code):
def predict(self, X):
scores = self.decision_function(X)
if len(scores.shape) == 1:
indices = (scores > 0).astype(np.int)
else:
indices = scores.argmax(axis=1)
return self.classes_[indices]
where the decision_function is given by:
def decision_function(self, X):
[...]
scores = safe_sparse_dot(X, self.coef_.T,
dense_output=True) + self.intercept_
return scores.ravel() if scores.shape[1] == 1 else scores
So for the two-dimensional case of your example this means that a data point is classified +1 if
x*w1 + y*w2 + i > 0
where
x, y = X
w1, w2 = self.coef_
i = self.intercept_
and -1 otherwise. So the decision depends on x*w1 + y*w2 + i being greater than or less than (or equal to) zero. Thus the "border" is found by setting x*w1 + y*w2 + i == 0. We are free to choose one of the components and the other one is determined by this equation.
The following snippet fits a SGDClassifier and plots the resulting "border". It assumes that the data points are scattered around the origin (x, y = 0, 0), i.e. that their mean is (approximately) zero. Actually, in order to obtain good results, one should first subtract the mean from the data points, then perform the fit and then add the mean back to the result. The following snippet just scatters the points around the origin.
import matplotlib.pyplot as plt
import numpy as np
from sklearn.linear_model import SGDClassifier
n = 100
x = np.random.uniform(-1, 1, size=(n, 2))
# We assume points are scatter around zero.
b = np.zeros(2)
d = np.random.uniform(-1, 1, size=2)
slope, intercept = (d[1] / d[0]), 0.
fig, ax = plt.subplots(figsize=(8,8))
ax.scatter(x[:, 0], x[:, 1], color = 'black')
ax.plot([b[0], d[0]], [b[1], d[1]], 'b-', label='Ideal')
labels = []
for point in x:
if(point[1] > (slope * point[0] + intercept)):
ax.annotate('+', xy=point, xytext=(0, -10), textcoords='offset points', color = 'blue', ha='center', va='center')
labels.append(1)
else:
ax.annotate('--', xy=point, xytext=(0, -10), textcoords='offset points', color = 'red', ha='center', va='center')
labels.append(-1)
labels = np.array(labels)
classifier = SGDClassifier()
classifier.fit(x, labels)
x1 = np.random.uniform(-1, 1)
x2 = (-classifier.intercept_ - x1 * classifier.coef_[0, 0]) / classifier.coef_[0, 1]
ax.plot([0, x1], [0, x2], 'g--', label='Fit')
plt.legend()
plt.show()
This plot shows the result for n = 100 data points:
The following plot shows the results for different n where the points have been chosen randomly from the pool which contains 1000 data points:
This is the answer I've come up with. Some notes I realised:
w = w + y(r) algorithm only works for normalised vectors. 'w' is the weight vector, 'r' is [x,y] of the point in question, 'y' is the sign of the label.
You can find the slope and intercept from the resulting vector 'w' by putting the coefficients in ax+by+c = 0 form and solving for 'y'.
w = np.array([0,0,0])
restart = True
while restart:
ii = 0
restart = False
for x,y in pts10:
if(restart == False):
ii += 1
r = np.array([x,y,1])
if (np.dot(w,r) >= 0) and int(label_dict['point{}'.format(ii)][1]) >= 0:
print "Point " + str(ii) + " is correctly above the line --> no adjustments"
elif (np.dot(w,r) < 0) and int(label_dict['point{}'.format(ii)][1]) < 0:
print "Point " + str(ii) + " is correctly below the line --> no adjustments"
elif (np.dot(w,r) >= 0) and int(label_dict['point{}'.format(ii)][1]) < 0:
print "Point " + str(ii) + " should have been below the line"
w = np.subtract(w,r)
restart = True
break
elif (np.dot(w,r) < 0) and int(label_dict['point{}'.format(ii)][1]) >= 0:
print "Point " + str(ii) + " should have been above the line"
w = np.add(w,r)
restart = True
break
else:
print "THERE IS AN ERROR, A POINT PASSED THROUGH HERE"
print w
slope_w = (-w[0])/w[1]
intercept_w = (-w[2])/w[1]

Classification perceptron implementation

I have written Percentron example in Python from here.
Here is the complete code
import matplotlib.pyplot as plt
import random as rnd
import matplotlib.animation as animation
NUM_POINTS = 5
LEANING_RATE=0.1
fig = plt.figure() # an empty figure with no axes
ax1 = fig.add_subplot(1,1,1)
plt.xlim(0, 120)
plt.ylim(0, 120)
points = []
weights = [rnd.uniform(-1,1),rnd.uniform(-1,1),rnd.uniform(-1,1)]
circles = []
plt.plot([x for x in range(100)], [x for x in range(100)])
for i in range(NUM_POINTS):
x = rnd.uniform(1, 100)
y = rnd.uniform(1, 100)
circ = plt.Circle((x, y), radius=1, fill=False, color='g')
ax1.add_patch(circ)
points.append((x,y,1))
circles.append(circ)
def activation(val):
if val >= 0:
return 1
else:
return -1;
def guess(pt):
vsum = 0
#x and y and bias weights
vsum = vsum + pt[0] * weights[0]
vsum = vsum + pt[1] * weights[1]
vsum = vsum + pt[2] * weights[2]
gs = activation(vsum)
return gs;
def animate(i):
for i in range(NUM_POINTS):
pt = points[i]
if pt[0] > pt[1]:
target = 1
else:
target = -1
gs = guess(pt)
error = target - gs
if target == gs:
circles[i].set_color('r')
else:
circles[i].set_color('b')
#adjust weights
weights[0] = weights[0] + (pt[0] * error * LEANING_RATE)
weights[1] = weights[1] + (pt[1] * error * LEANING_RATE)
weights[2] = weights[2] + (pt[2] * error * LEANING_RATE)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
I expect the points plotted on graph to classify themselves to red or blue depending on expected condition (x coordinate > y coordinate) i.e. above or below reference line (y=x)
This does not seem to work and all points go red after some iterations.
What am I doing wrong here. Same is working in youtube example.
I looked at your code and the video and I believe the way your code is written, the points start out as green, if their guess matches their target they turn red and if their guess doesn't match the target they turn blue. This repeats with the remaining blue eventually turning red as their guess matches the target. (The changing weights may turn a red to blue but eventually it will be corrected.)
Below is my rework of your code that slows down the process by: adding more points; only processing one point per frame instead of all of them:
import random as rnd
import matplotlib.pyplot as plt
import matplotlib.animation as animation
NUM_POINTS = 100
LEARNING_RATE = 0.1
X, Y = 0, 1
fig = plt.figure() # an empty figure with no axes
ax1 = fig.add_subplot(1, 1, 1)
plt.xlim(0, 120)
plt.ylim(0, 120)
plt.plot([x for x in range(100)], [y for y in range(100)])
weights = [rnd.uniform(-1, 1), rnd.uniform(-1, 1)]
points = []
circles = []
for i in range(NUM_POINTS):
x = rnd.uniform(1, 100)
y = rnd.uniform(1, 100)
points.append((x, y))
circle = plt.Circle((x, y), radius=1, fill=False, color='g')
circles.append(circle)
ax1.add_patch(circle)
def activation(val):
if val >= 0:
return 1
return -1
def guess(point):
vsum = 0
# x and y and bias weights
vsum += point[X] * weights[X]
vsum += point[Y] * weights[Y]
return activation(vsum)
def train(point, error):
# adjust weights
weights[X] += point[X] * error * LEARNING_RATE
weights[Y] += point[Y] * error * LEARNING_RATE
point_index = 0
def animate(frame):
global point_index
point = points[point_index]
if point[X] > point[Y]:
answer = 1 # group A (X > Y)
else:
answer = -1 # group B (Y > X)
guessed = guess(point)
if answer == guessed:
circles[point_index].set_color('r')
else:
circles[point_index].set_color('b')
train(point, answer - guessed)
point_index = (point_index + 1) % NUM_POINTS
ani = animation.FuncAnimation(fig, animate, interval=100)
plt.show()
I tossed the special 0,0 input fix as it doesn't apply for this example.
The bottom line is that if everything is working, they should all turn red. If you want the color to reflect classification, then you can change this clause:
if answer == guessed:
circles[point_index].set_color('r' if answer == 1 else 'b')
else:
circles[point_index].set_color('g')
train(point, answer - guessed)

Dynamic time warping with python (final mapping)

I need to align two sound signals in order to map one into the other (both signals corresponds to the same behavior). I try to implement the python code from:
https://nipunbatra.github.io/blog/2014/dtw.html
as a function to be called by my code. An example:
#time warping sound function trial
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
my_path ='/home/...'
def time_warping (x,y,fs,name):
distances = np.zeros((len(y), len(x)))
accumulated_cost = np.zeros((len(y), len(x)))
accumulated_cost[0,0] = distances[0,0]
def distance_cost_plot(distances):
#function to visualize the distance matrix
im = plt.imshow(distances, interpolation='nearest', cmap='Reds')
plt.gca().invert_yaxis()
plt.xlabel("X")
plt.ylabel("Y")
plt.grid()
plt.colorbar();
#plt.show()
plt.close()
def path_cost(x, y, accumulated_cost, distances):
#this is like mlpy.dtw_std (I gues..)
path = [[len(x)-1, len(y)-1]]
cost = 0
i = len(y)-1
j = len(x)-1
while i>0 and j>0:
if i==0:
j = j - 1
elif j==0:
i = i - 1
else:
if accumulated_cost[i-1, j] == min(accumulated_cost[i-1, j-1], accumulated_cost[i-1, j], accumulated_cost[i, j-1]):
i = i - 1
elif accumulated_cost[i, j-1] == min(accumulated_cost[i-1, j-1], accumulated_cost[i-1, j], accumulated_cost[i, j-1]):
j = j-1
else:
i = i - 1
j= j- 1
path.append([j, i])
path.append([0,0])
for [y, x] in path:
cost = cost +distances[x, y]
return path, cost
#Here I apply the function over function x and y
path, cost = path_cost(x, y, accumulated_cost, distances)
for i in range(len(y)):
for j in range(len(x)):
distances[i,j] = (x[j]-y[i])**2
#Here I plot the distance
g=distance_cost_plot(distances)
accumulated_cost = np.zeros((len(y), len(x)))
accumulated_cost[0,0] = distances[0,0]
for i in range(1, len(y)):
accumulated_cost[i,0] = distances[i, 0] + accumulated_cost[i-1, 0]
for i in range(1, len(x)):
accumulated_cost[0,i] = distances[0,i] + accumulated_cost[0, i-1]
for i in range(1, len(y)):
for j in range(1, len(x)):
accumulated_cost[i, j] = min(accumulated_cost[i-1, j-1], accumulated_cost[i-1, j], accumulated_cost[i, j-1]) + distances[i, j]
#empy list for the maping
map_x_final =[]
map_y_final =[]
map_x_f_final =[]
map_y_f_final =[]
paths = path_cost(x, y, accumulated_cost, distances)[0] #no entiendo la sintaxis de esta linea
print 'path',paths
print 'accumulated_cost',accumulated_cost
print 'distances',distances
#print 'paths.shape',path.shape
plt.figure(figsize=(14,8)) # 8 plots in one
plt.subplot(2,1,1)
grid(True)
map_x_fx =[]
map_y_fy =[]
map_y_fy_newlist =[]
for [map_x, map_y] in paths:
#print map_x, x[map_x], ":", map_y, y[map_y]
plt.plot([map_x*float(1)/float(fs), map_y*float(1)/float(fs)], [x[map_x], y[map_y]], 'r')
#plt.plot([map_x, map_y], [x[map_x], y[map_y]], 'r')
#saving in empy list
map_x_fx.append([map_x,x[map_x]])
map_y_fy.append([map_x,y[map_y]])
map_x_final.append(map_x)
map_y_final.append(map_y)
map_x_f_final.append(x[map_x])
map_y_f_final.append(y[map_y])
dif_a_sumar = (map_y-map_x)*float(1)/float(fs)
map_x_final = np.asarray(map_x_final)
map_y_final = np.asarray(map_y_final)
map_x_f_final = np.asarray(map_x_f_final)
map_y_f_final = np.asarray(map_y_f_final)
####
map_x_final_vec = np.asarray(map_x_fx)
map_y_final_vec = np.asarray(map_y_fy)
#Erase the elements that has been alrady map
lista_aux=[]
for j,[a,b] in enumerate(map_y_fy):
print j,':', [a,b]
print len( map_x_final[:j])
if a not in map_x_final[:j]:
lista_aux.append([a,b])
else:
pass
print'++++++'
print'lista aux len: ',len(lista_aux)
map_y_final_vec_ =np.asarray(lista_aux)
print'++++'
print 'map_y_fy',len(map_y_fy)
print'*************************'
#print ' a veer map_x_fx: ',map_x_fx
#print ' a veer map_x_fx type: ',type(map_x_fx)
#print ' map_y_f_final_vec shape',map_y_f_final_vec.shape
#print ' a veer map_x_final_vec: ',map_x_final_vec
#print ' a veer map_x_final_vec[0]: ',map_x_final_vec[0]
print'*************************'
print 'x shape',x.shape
print 'y shape',y.shape
print 'map_x_f_final',map_x_f_final.shape
print 'map_y_f_final',map_y_f_final.shape
print 'map_y_final_vec shape',map_y_final_vec.shape
print 'map_y_final_vec_ shape',map_y_final_vec_.shape
print'*************************'
#print map_x_final.size, map_y_final.size, map_x_f_final.size, map_y_f_final.size
time_x = np.arange(x.size)*float(1)/float(fs)
time_y = np.arange(y.size)*float(1)/float(fs)
time_map_x = np.arange(map_x_f_final.size)*float(1)/float(fs)
time_map_y = np.arange(map_y_f_final.size)*float(1)/float(fs)
plt.plot(time_x,x, 'bo-',linewidth=1 ,label='funcion target: X ')#'bo-'
plt.plot(time_y,y, 'go-',linewidth=1,markersize=3, label = 'funcion a proyectar :Y')#'g^-'
plt.legend(fontsize= 'small')
plt.ylabel('Signal')
plt.xlabel('time [s]')
plt.subplot(2,1,2) #los graficos mapeados
grid(True)
plt.plot(time_x,x, 'b',linewidth=1 ,label='funcion target: X sonido-vs')#o-
plt.plot(time_y,y, 'g',linewidth=1,markersize=3, label = 'funcion a proyectar :Y sonido-p')#'g^-'
plt.plot(map_y_final_vec_[:, 0]*float(1)/float(fs), map_y_final_vec_[:,1],'yo-',markersize=5, label='funcion Y mapeada donde convergen con DTW sobre X')#'m^'
plt.ylabel('Signal')
plt.xlabel('time [s]')
plt.legend(fontsize= 'small')
figname = "%s.jpg"%('alineado_dtw_'+name)
plt.savefig(my_path+figname,dpi=200)
#plt.show()
plt.close()
mapeo_time = map_y_final_vec_[:, 0]*float(1)/float(fs)
mapeo_amplitude = map_y_final_vec_[:,1]
return mapeo_time, mapeo_amplitude
I am able to obtain the distance between both signals:
But I'm not sure with the final mapping.
Am I doing something wrong with my mapping? I need to project one signal over the other, rescaling the first one with the other. I also tried with these two real signals:
I try to compare with:https://pypi.python.org/pypi/fastdtw and also with mlp library, but I get different signal mapping.
I also put everything on https://github.com/katejarne/dtw with the data set to generate the last figure and the mapping.

Categories