def fourier_intro(time, data, freq, Pxx, time_slice, out_file): """Time- and frequency presentation of sound. Corresponds to Fig. 9.1 in the book""" fig, axs = plt.subplots(1, 3) axs[0].plot(time, data, lw=0.5) axs[0].set_xlabel('Time (s)') axs[0].set_ylabel('Sound-pressure ()') axs[0].margins(x=0) axs[1].plot(time, data) axs[1].set_xlim(time_slice) axs[1].set_xlabel('Time (s)') axs[1].set_yticklabels([]) axs[2].plot(freq, np.sqrt(Pxx)) axs[2].set_xlim([0, 4000]) axs[2].set_xlabel('Frequency (Hz)') axs[2].set_ylabel('|FFT| ()') axs[2].ticklabel_format(style='sci', scilimits=(0, 4)) # Position the plots axs[0].set_position([0.125, 0.11, 0.2, 0.775]) axs[1].set_position([0.35, 0.11, 0.2, 0.775]) show_data(out_file)
def noise_effects(time, sound, Pxx, time_slice, duration, out_file): """Effect of adding noise to a signal on the powerspectrum Corresponds to Fig. 9.8 in the book """ # Note that a1 has been normalized to have a range of 1 sound_noisy = sound + 1 / 100 * np.random.randn(len(sound)) fft_noisy = np.fft.fft(sound_noisy) Pxx_noisy = np.abs(fft_noisy)**2 # Normalize to 'density' Pxx_noisy = Pxx_noisy * 2 / (np.sum(Pxx_noisy) / duration) fig, axs = plt.subplots(1, 2) axs[0].plot(time, sound, label='original') axs[0].plot(time, sound_noisy, label='noise added') axs[0].set_xlabel('Time (s)') axs[0].set_ylabel('Sound-pressure ()') axs[0].set_xlim(time_slice) axs[0].legend() axs[1].semilogy(freq, Pxx, label='original', lw=0.5) axs[1].semilogy(freq, Pxx_noisy, label='noise added', lw=0.5) axs[1].set_xlabel('Frequency (Hz)') axs[1].set_ylabel('Powerspectrum (P**2/Hz)') axs[1].set_xlim([1200, 1700]) plt.tight_layout() show_data(out_file)
def main(): # Generate dummy data x = np.array([-2.1, -1.3, -0.4, 1.9, 5.1, 6.2]) # Define the two plots fig, axs = plt.subplots(1, 2, sharey=True) # Generate the left plot plot_histogram(axs[0], x) # Generate the right plot explain_KDE(axs[1], x) # Save and show show_data('KDEexplained.jpg')
def show3D() -> None: """ Generate 3D plots. """ # imports specific to the plots in this example from matplotlib import cm # colormaps # This module is required for 3D plots! from mpl_toolkits.mplot3d import Axes3D # Twice as wide as it is tall. fig = plt.figure(figsize=plt.figaspect(0.5)) set_fonts(16) #---- First subplot # Generate the data X = np.arange(-5, 5, 0.1) Y = np.arange(-5, 5, 0.1) X, Y = np.meshgrid(X, Y) R = np.sqrt(X**2 + Y**2) Z = np.sin(R) # Note the definition of "projection", required for 3D plots #plt.style.use('ggplot') ax = fig.add_subplot(1, 2, 1, projection='3d') surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.GnBu, linewidth=0, antialiased=False) #surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.viridis_r, #linewidth=0, antialiased=False) ax.set_zlim3d(-1.01, 1.01) fig.colorbar(surf, shrink=0.5, aspect=10) #---- Second subplot # Get some 3d test-data from mpl_toolkits.mplot3d.axes3d import get_test_data ax = fig.add_subplot(1, 2, 2, projection='3d') X, Y, Z = get_test_data(0.05) ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10) show_data('3dGraph.jpg')
def bode_log(out_file): """Logarithmic presentation of transfer function """ fig, axs = plt.subplots(2, 1, sharex=True) # Indicate the approximate gains axs[0].plot([0, 1 / tau], [0, 0], ls='dashed', lw=3, color='C1') axs[0].plot([1 / tau, 10 / tau], [0, -20], ls='dashed', lw=3, color='C1') axs[0].set_xlim(1e-2, 1) # Bode magnitude plot axs[0].semilogx(w, mag, color='C0') axs[0].plot(w[index], mag[index], 'ro') # Indicate cut-off frequency # Bode phase plot axs[1].semilogx(w, phase) axs[1].plot(w[index], phase[index], 'ro') # Format the plots axs[0].set_ylabel('Gain [dB]') axs[0].grid(True, ls='dotted') axs[0].margins(x=0) axs[0].annotate(r'$\omega = \frac{1}{\tau}$', xy=(w[index], mag[index]), xycoords='data', xytext=(-10, -50), textcoords='offset points', fontsize=16, arrowprops=dict(arrowstyle="->", connectionstyle="arc3")) axs[1].set_xlabel('Frequency [Hz]') axs[1].set_ylabel('Phase [deg]') axs[1].set_yticks([0, -45, -90], minor=False) axs[1].margins(x=0) axs[1].grid(True, ls='dotted') axs[1].annotate(r'$\omega = \frac{1}{\tau}$', xy=(w[index], phase[index]), xycoords='data', xytext=(-10, 30), textcoords='offset points', fontsize=16, arrowprops=dict(arrowstyle="->", connectionstyle="arc3")) show_data(out_file)
def linear_and_log(freq, Pxx, out_file): """Comparison of linear and log representatino of powerspectrum Corresponds to Fig. 9.7 in the book. """ fig, axs = plt.subplots(1, 2) upper_limit = 4000 axs[0].plot(freq, Pxx) axs[0].set_xlim([0, upper_limit]) axs[0].set_xlabel('Frequency (Hz)') axs[0].set_ylabel('Powerspectrum ()') axs[0].ticklabel_format(style='sci', scilimits=(0, 4)) axs[1].semilogy(freq, Pxx, lw=0.5) axs[1].set_xlim([0, upper_limit]) axs[1].set_xlabel('Frequency (Hz)') axs[1].set_ylabel('Powerspectrum (dB)') plt.tight_layout() show_data(out_file)
def bode_linear(out_file): """Linear presentation of transfer function """ fig, axs = plt.subplots(2, 1, sharex=True) # Bode magnitude plot axs[0].plot(w, 10**(mag / 20)) axs[0].plot(w[index], 10**(mag[index] / 20), 'ro') # Bode phase plot axs[1].plot(w, phase) axs[1].plot(w[index], phase[index], 'ro') # Format the plots axs[0].set_ylabel('Gain [linear]') axs[0].set_ylim([-0.10, 1.1]) axs[0].margins(x=0) axs[0].grid(True, ls='dotted') axs[0].annotate(r'$\omega = \frac{1}{\tau}$', xy=(w[index], 10**(mag[index] / 20)), xycoords='data', xytext=(10, 20), textcoords='offset points', fontsize=16, arrowprops=dict(arrowstyle="->", connectionstyle="arc3")) axs[1].set_xlabel('Frequency [Hz]') axs[1].set_ylabel('Phase [deg]') axs[1].margins(x=0) axs[1].set_yticks([0, -45, -90], minor=False) axs[1].grid(True, ls='dotted') axs[1].annotate(r'$\omega = \frac{1}{\tau}$', xy=(w[index], phase[index]), xycoords='data', xytext=(10, 30), textcoords='offset points', fontsize=16, arrowprops=dict(arrowstyle="->", connectionstyle="arc3")) show_data(out_file)
def feedback(tau: float, fb_gain: float) -> None: """Simulation of a negative feedback system, using the package 'control'. 'time', 'in_signal', 'num' and 'den' are taken from the global workspace Parameters ---------- tau : time constant of a first order lag [sec] fb_gain : feedback gain """ # First, define the feedforward transfer function sys = control.TransferFunction(num, den) print(sys) # 1 / (tau*s + 1) # Simulate the response of the feedforward system t_ff, out_ff = control.forced_response(sys, T=time, U=in_signal) # Then define a feedback-loop, with gain fb_gain sys_total = control.feedback(sys, fb_gain) print(sys_total) # 1 / (tau*s + (1+k)) # Simulate the response of the feedback system t_fb, out_fb = control.forced_response(sys_total, T=time, U=in_signal) # Show the signals plt.plot(time, in_signal, '-', label='Input') plt.plot(t_ff, out_ff, '--', label='Feedforward') plt.plot(t_fb, out_fb, '-.', label='Feedback') # Format the plot plt.xlabel('Time [sec]') plt.title(f"First order lag (tau={tau} sec)") print('Simulated with "control"') plt.legend() out_file = 'feedback.jpg' show_data(out_file)
def welch_periodogram(data, rate, freq, Pxx, out_file): """Comparison of simple powerspectrum to Welch-periodogram Corresponds to Fig. 9.9 in the book. """ fig, axs = plt.subplots(1, 2) f, welch = signal.welch(data, fs=rate, nperseg=len(data) / 8) df = np.diff(f)[0] # "normalize" welch welch = welch / (np.sum(welch) * df) axs[0].semilogy(freq, Pxx, label='Periodogram', lw=0.5) axs[0].semilogy(f, welch, label='Welch', lw=0.8) axs[0].set_xlim([0, rate / 2]) axs[0].set_ylabel('Powerspectrum (P**2/Hz)') axs[0].set_xlabel('Frequency (Hz)') axs[1].semilogy(freq, Pxx, label='Pxx') axs[1].semilogy(f, welch, label='Welch') axs[1].set_xlim([800, 1100]) axs[1].set_xlabel('Frequency (Hz)') axs[1].legend() show_data(out_file)
# Add the text fs = 18 plt.text(11, 5.5, '$\pm$ 1SD', fontsize=fs, fontstyle='italic', color='C0') plt.text(62, 5.2, '$\pm$ 1SEM', fontsize=fs, fontstyle='italic', color='C1') # plt.annotate('mean', (110,np.mean(x)),xycoords='data', # fontsize=fs, xytext=(110, 5.5), textcoords='data', # arrowprops={ # 'facecolor':'black', # 'shrink':1, # 'headwidth':6, # 'headlength':6, # 'width': 1}) plt.annotate('mean', (-5,np.mean(x)),xycoords='data', fontsize=fs, xytext=(-40, 4.5), textcoords='data', color = [0.6, 0.6, 0.6], arrowprops={ 'color':[0.6, 0.6, 0.6], 'shrink':1, 'headwidth':6, 'headlength':6, 'width': 1}) # Save and show plt.tight_layout() outFile = 'standardError.jpg' show_data(outFile, out_dir='.')
# Import the required libraries import numpy as np import matplotlib.pyplot as plt from skimage import data from utilities.my_style import set_fonts, show_data # Get a color-image img = data.astronaut() nrows, ncols = img.shape[:2] # Make vectors from 1 to 0, with lengths matching the image alpha_row = np.linspace(1, 0, ncols) alpha_col = np.linspace(1, 0, nrows) # Make coordinate-grids X, Y = np.meshgrid(alpha_row, alpha_col) # Scale the vector from 0 to 255, and # let the image fade from top-right to bottom-left X_Y = np.uint8(X * Y * 255) X_Y = np.atleast_3d(X_Y) #make sure the dimensions matches the image # Add the alpha-layer img_alpha = np.concatenate((img, X_Y), axis=2) plt.imshow(img_alpha) out_file = 'fading_astronout.png' show_data(out_file)
# Generate the base image data = np.zeros((99, 99)) data[34:66, 33:67] = 1 data[85:87, 85:87] = 1 data[49:51, 49:51] = 0 # Show the original image fig, ax = plt.subplots() plt.gray() ax.imshow(data) ax.set_title('Original Image') ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) out_file = 'Square.jpg' show_data(out_file) # Perform the morphological operations selem = morphology.square(5) fig, axs = plt.subplots(2, 2, figsize=(8, 8)) show_modImage(data, 'binary_erosion', axs[0][0], 'Eroded') show_modImage(data, 'binary_dilation', axs[0][1], 'Dilated') show_modImage(data, 'binary_opening', axs[1][0], 'Opened (Dilation after Erosion)') show_modImage(data, 'binary_closing', axs[1][1], 'Closed (Erosion after Dilation)') out_file = 'Square_Morphological.jpg' show_data(out_file, out_dir='.')
def simplePlots() -> None: """ Demonstrate the generation of different statistical standard plots. """ # Univariate data ------------------------- # Make sure that always the same random numbers are generated np.random.seed(1234) # Generate data that are normally distributed x = np.random.randn(500) # Other graphics settings # Set " context='poster' " for printouts, and "set_fonts(32)" sns.set(context='notebook', style='ticks', palette='muted') # Set the fonts the way I like them set_fonts(16) # Scatter plot plt.plot(x, '.', markersize=7) plt.xlim([0, len(x)]) # Save and show the data, in a systematic format printout('scatterPlot.jpg', xlabel='Datapoints', ylabel='Values', title='Scatter') # Histogram plt.hist(x) printout('histogram_plain.jpg', xlabel='Data Values', ylabel='Frequency', title='Histogram, default settings') plt.hist(x, 25, density=True) printout('density_histogram.jpg', xlabel='Data Values', ylabel='Probability', title='Density Histogram, 25 bins') # Boxplot # The ox consists of the first, second (middle) and third quartile set_fonts(18) plt.boxplot(x, sym='*') printout('boxplot.jpg', xlabel='Values', title='Boxplot') plt.boxplot(x, sym='*', vert=False) plt.title('Boxplot, horizontal') plt.xlabel('Values') plt.show() # Errorbars x = np.arange(5) y = x**2 errorBar = x / 2 plt.errorbar(x, y, yerr=errorBar, fmt='o', capsize=5, capthick=3) plt.xlim([-0.2, 4.2]) plt.ylim([-0.2, 19]) printout('Errorbars.jpg', xlabel='Data Values', ylabel='Measurements', title='Errorbars') # SD for two groups weight = {'USA': 89, 'Austria': 74} weight_SD_male = 12 plt.errorbar([1, 2], weight.values(), yerr=weight_SD_male * np.r_[1, 1], capsize=5, LineStyle='', marker='o') plt.xlim([0.5, 2.5]) plt.xticks([1, 2], weight.keys()) plt.ylabel('Weight [kg]') plt.title('Adult male, mean +/- SD') show_data('SD_groups.jpg', out_dir='.') # Barplot # The font-size is set such that the legend does not overlap with the data np.random.seed(1234) set_fonts(16) df = pd.DataFrame(np.random.rand(7, 3), columns=['one', 'two', 'three']) df.plot(kind='bar', grid=False, color=sns.color_palette('muted')) show_data('barplot.jpg') # Bivariate Plots df2 = pd.DataFrame(np.random.rand(50, 3), columns=['a', 'b', 'c']) df2.plot(kind='scatter', x='a', y='b', s=df2['c'] * 500) plt.axhline(0, ls='--', color='#999999') plt.axvline(0, ls='--', color='#999999') printout('bivariate.jpg') sns.set_style('ticks') # Pieplot txtLabels = 'Cats', 'Dogs', 'Frogs', 'Others' fractions = [45, 30, 15, 10] offsets = (0, 0.05, 0, 0) plt.pie(fractions, explode=offsets, labels=txtLabels, autopct='%1.1f%%', shadow=True, startangle=90, colors=sns.color_palette('muted')) plt.axis('equal') printout('piePlot.jpg', title=' ')
# Formatting set_fonts(14) # Noisy sine t = np.arange(0, 20, 0.05) x = np.sin(t) x_noisy = x + 0.06 * t + 0.3 * np.random.randn(len(x)) #plt.plot(t, x) #show_data('sine.jpg') fig, axs = plt.subplots(1, 2) axs[0].plot(t, x_noisy) axs[1].plot(t, x) show_data('noisy_sine.svg') # BW-Image img_file = '../../data/HagenbergRocks.jpg' img = color.rgb2gray(plt.imread(img_file)) # Edges edges = feature.canny(img, sigma=2.5) fig, ax = plt.subplots(1, 1) #axs[0].imshow(img, cmap='gray') #axs[0].axis('off') ax.imshow(edges, cmap='gray') ax.axis('off')
fs = 18 plt.text(11, 5.5, '$\pm$ 1SD', fontsize=fs) plt.text(62, 5.2, '$\pm$ 1SEM', fontsize=fs) # plt.annotate('mean', (110,np.mean(x)),xycoords='data', # fontsize=fs, xytext=(110, 5.5), textcoords='data', # arrowprops={ # 'facecolor':'black', # 'shrink':1, # 'headwidth':6, # 'headlength':6, # 'width': 1}) plt.annotate('mean', (-5, np.mean(x)), xycoords='data', fontsize=fs, xytext=(-40, 4.5), textcoords='data', color=[0.6, 0.6, 0.6], arrowprops={ 'color': [0.6, 0.6, 0.6], 'shrink': 1, 'headwidth': 6, 'headlength': 6, 'width': 1 }) # Save and show outFile = ('standardError.jpg') show_data(outFile)
def power_spectrum(t: np.ndarray, dt: float, sig: np.ndarray, sig_ideal: np.ndarray) -> None: """ Demonstrate three different ways to calculate the power-spectrum Parameters ---------- t : time [sec] dt : sample period [sec] sig : sample signal to be analyzed sig_ideal : signal without noise """ set_fonts(16) fig, axs = plt.subplots(1,2, figsize=(10, 5)) if latex_installed: txt ='$\displaystyle signal=offset + \sum_{i=0}^{2} a_i*sin(\omega_i*t) + noise$' label = '$|FFT|\; ()$' label2 = '$|FFT|^2 ()$' else: txt = 'signal = offset + sum(i=0:2) a_i*sin(omega_i*t)' label = '|FFT| ()' label2 = '|FFT|^2 ()' # From a quick look we learn - nothing axs[0].plot(t, sig, lw=0.7, label='noisy') axs[0].plot(t, sig_ideal, ls='dashed', lw=2, label='ideal') axs[0].set_xlim(0, 0.4) axs[0].set_ylim(-15, 25) axs[0].set_xlabel('Time (s)') axs[0].set_ylabel('Signal ()') axs[0].legend() axs[0].text(0.2, 24, s=txt, fontsize=16) # Calculate the spectrum by hand fft = np.fft.fft(sig) print(fft[:3]) fft_abs = np.abs(fft) # The easiest way to calculate the frequencies freq = np.fft.fftfreq(len(sig), dt) axs[1].plot(freq, fft_abs) axs[1].set_xlim(0, 35) axs[1].set_xlabel('Frequency (Hz)') axs[1].set_ylabel(label) axs[1].set_yticklabels([]) show_data('FFT_sines.jpg') # Also show the double-sided spectrum fig, ax = plt.subplots(1,1) ax.plot(fft_abs) #ax.set_xlim(0, 35) ax.set_xlabel('Points') ax.set_ylabel(label) plt.tight_layout() show_data('FFT_doublesided.jpg') # With real input, the power spectrum is symmetrical and we only one half fft_abs = fft_abs[:int(len(fft_abs)/2)] freq = freq[:int(len(freq)/2)] # The power is the norm of the amplitude squared Pxx = fft_abs**2 # Showing the same data on a linear and a log scale fig, axs = plt.subplots(2,1, sharex=True) axs[0].plot(freq, Pxx) axs[0].set_ylabel('Power (linear)') axs[1].semilogy(freq, Pxx) axs[1].set_xlabel('Frequency (Hz)') axs[1].set_ylabel('Power (dB)') show_data('FFT_sines_power_lin_log.jpg') # Periodogram and Welch-Periodogram f_pgram, P_pgram = signal.periodogram(sig, fs = 1/dt) f_welch, P_welch = signal.welch(sig, fs = 100, nperseg=2**8) fig, axs = plt.subplots(2, 1, sharex=True) axs[0].semilogy(f_pgram, P_pgram, label='periodogram') axs[1].semilogy(f_welch, P_welch, label='welch') axs[0].set_ylabel('Spectral Density (dB)') axs[0].legend() axs[0].set_ylim(1e-4, 1e3) axs[1].set_xlabel('Frequency (Hz)') axs[1].set_ylabel('Spectral Density (dB)') axs[1].legend() show_data('FFT_sines_periodogram.jpg')
if __name__ == '__main__': # Generate the data signal = np.zeros(20) signal[7:10] = 1 signal[14:17] = 1 feature = np.zeros(7) feature[2:5] = 1 # Plot the auto-correlation set_fonts(14) fig, axs = plt.subplots(2, 1) axs[0].plot(signal, 'o-') axs[0].hlines(0, -0.5, 20, ls='dotted') axs[0].set_xticks(np.arange(0, 21, 2)) axs[0].set_xlim(-0.5, 20) axs[0].set_ylabel('Signal') axs[0].set_yticks([0, 0.5, 1]) axs[1].plot(feature, 'o-') axs[1].hlines(0, -0.5, 20, ls='dotted') axs[1].set_xticks(np.arange(0, 20, 2)) axs[1].set_xticks(np.arange(0, 21, 2)) axs[1].set_xlim(-0.5, 20) axs[1].set_xlabel('Shift') axs[1].set_ylabel('Feature') axs[1].set_yticks([0, 0.5, 1]) sig_file = 'CorrDemo.jpg' show_data(sig_file, out_dir='.')