Matlab : save a spectrogram in a variable and display it - matlab

What I want is quite simple, or so I thought. I have many spectograms to compute and display which is time consuming to compute, so I want to save them in variables to display them without redoing the computation. The problem is that I can't find a way to plot them like they appear if I directly use the function spectogram().
Example :
sampling_rate = 100;
spectrogram(data,100,20,[],sampling_rate,'yaxis');
caxis([-20 60])
This displays a spectogram exactly as I want it :
I read the doc and I understand that I can save the results by doing something like this :
[S,F,T] = spectrogram(data,100,20,[],sampling_rate);
Also, I know that the function spectogram internally calls surf().
I found this post that seems to give a solution to my problem by doing this :
[S,F,T] = spectrogram(data,100,20,[],sampling_rate);
surf(T,F,abs(S),'EdgeColor','none');
axis tight; view(0,90);
But I get this plot, which is far from what I expected:
The axis labels and colorbar are gone, and the colors are completely not scaled. If I do this manually by adding colorbar; caxis([-20 60]); as previously, I get this thing:
Isn't there a simple solution to save a spectogram and display it on command ?
Like S = spectogram(...) then plot(S)?

If the OP's answer works more or less as expected, then the following shorter code should do the same:
[S,F,T,P] = spectrogram(data(1:3000),100,20,[],sampling_rate);
h = imagesc(T,F,10*log10(P),[-20,60]);
xlabel('Time (secs)')
ylabel('Frequency (Hz)')
colorbar;
(I don't have the data to test it with.) Note that here the x and y ticks are set automatically. If you want to select seconds, minutes, hours depending on the scale of T, you could do:
lab = 'Time (secs)';
if T(end) > 60*60*3
T = T/(60*60);
lab = 'Time (hours)';
elseif T(end) > 60*3
T = T/60;
lab = 'Time (mins)'
end
h = imagesc(T,F,10*log10(P),[-20,60]);
xlabel(lab)
ylabel('Frequency (Hz)')
colorbar;

I found a beginning of solution.
If I write :
[S,F,T,P] = spectrogram(data(1:3000),100,20,[],sampling_rate);
P saves the spectral power density in a matrix, which is quite convenient to plot without forgetting to add 10*log10(P):
h = pcolor(10*log10(P));
colorbar;
caxis([-20 60]);
set(h,'EdgeColor','none')
And I get this which is much better:
But I'm still missing the time and frequency axis, because it currently displays the dimension of the matrix P.
Edit:
I finally found some sort of solution which would be dealing with the axis manually. The display of the time is still terrible and I'm certain there is a better way but here we go:
[S,F,T,P] = spectrogram(data(1001:3000),100,50,[],sampling_rate);
h = pcolor(10*log10(P));
cb = colorbar;
caxis([-20 60]);
ylabel(cb, 'Power/frequency (dB/Hz')
set(h,'EdgeColor','none')
xticks(0:round(size(P,2)/6):size(P,2))
xt = xticks;
xticklabels([T(xt(2)) T(xt(2:end))]);
xlabel('Time (secs)');
yticks([0:13:size(P,1) size(P,1)])
yt = yticks;
yticklabels(0:5:50);
ylabel('Frequency (Hz)')
The time is saved in the vector T which is decently convenient in order to apply it on the x label. The frequency is stored F but in my case it'll always be between 0-50Hz so I wrote it manually.
I hope it'll help some people, and if you know how to properly set the time axis with automatic labelisation (sec, min, hours set automatically depending on the duration), by all means I would love to know.

Related

Logarithmic x axis in a stackplot MatLab

I'm trying to make a stack plot from a table, to present several variables with the same x-axis. however, I want the x-axis to be logarithmic. I couldn't find any way in stackplot documentation. Does anyone have any suggestions on how I can solve this?
I have tried using subplots instead, however, that way my graphs would not fit all on one page and I would have a lot of white space between the subplots. Therefore, I chose stackplot to make it more nice and less space-consuming.
tbl = readtable('usage.csv');
newYlabels = {'Heating (kWh/year)','Cooling (kWh/year)','Electricity (kWh/year)'};
stackedplot(tbl,[{2:16},{17:27},{28:35}],'XVariable',[1],'DisplayLabels',newYlabels);
Here is the output of the code:
Here is an image of what I'm trying to make, but the x-axis needs to be the real variable (\beta) in logarithmic scale
stackedplot has unfortunately no logarithmic axes option, and since it creates a StackedAxes instead of a normal Axes object, there is no way to changes this.
If the only reason you want to use stackedplot is to have less white-space, you might want to check out tight_subplot on the Matlab FEX. This would allow you to just do: set(ax, 'XScale', 'log').
You can however take the log of your x-data, and add that to the table:
tbl = readtable('outages.csv'); % sample data
tbl = sortrows(tbl, 'OutageTime'); % sort by date
% make x vector; for example just row numbers
x = (1:size(tbl,1)).';
xlog = log10(x);
% add x to table
tbl.Xlog = xlog;
tbl.X = x;
% plot normal x
f = figure(1); clf;
s = stackedplot(tbl, {'Loss'}, 'XVariable', 'X');
xlabel('rows');
% plot log(x)
f = figure(2); clf;
s = stackedplot(tbl, {'Loss'}, 'XVariable', 'Xlog');
xlabel('log(rows)')
Normal:
Log:

How to make matlab graphs look better

I have to use these graphs for a powerpoint presentation and I wanted to know how I could spruce it up to make it look more presentable and inviting. I can't even change the fontsize. Anything that can make the graph look more inviting will get the thumbs up as the answer.This is done in matlab by the way.
a = load('2small1big_heating');
m = load('2small1big_cooling');
xdata = m(:,5)
ydata = m(:,4)
pPoly = polyfit(xdata, ydata, 1); % Linear fit of xdata vs ydata
linePointsX = [min(xdata) max(xdata)]; % find left and right x values
linePointsY = polyval(pPoly,[min(xdata),max(xdata)]); % find y valuesfigure(1)
plot(m(:,5),m(:,4)/6269,'bo')
hold on
plot(a(:,5),a(:,4)/6269,'ro')
title('2Small1Big- Heating and Cooling')
legend('Cooling','Heating')
ylabel('Potential Energy (eV)');
xlabel('Temperature (K)');
Thanks.
Here's a few things that I find myself doing every time I need a plot to look presentable.
Change the fontsize to 14.
set(gca,'fontsize',14);
Make linewidth larger and/or markersize larger
plot(x,y,'r','linewidth',2);
plot(x,2*y,'b.','Markersize',18);
Sometimes turn grid on
grid on
To put it all together
x = 1:20;
y = rand(1,20).*x;
figure; hold on;
set(gca,'fontsize',14);
a = plot(x,y,'r','linewidth',2);
plot(x,2*y,'b.','Markersize',18);
grid on
xlabel('X (x units)');
ylabel('Important Stuff');
title('VERY IMPORTANT PLOT');
Most importantly - change to the still undocumented and yet not officially supported HG2-Graphics-Engine. The improvements you get are better than anything else you could achieve with the "old" functionality.
It already works really nice, I'm not seeing a lot of bugs. There are issues, but they are solvable.
Apart from that, you could use nicer fonts, especially if you want to use the plots in combination with Latex. You can set it globally, as well as the fontsize:
set(0,'defaultAxesFontName', 'CMU Serif Roman')
set(0,'defaultAxesFontSize', 12)
Also use the Latex-Interpreter for your labels:
y_label = {'$$ \mathrm{Mag} ( G ) \rm ~in~ dB$$','interpreter','latex';
x_label = {'$$ f \rm ~in~ Hz$$','interpreter','latex'};
With some easy steps, you get much better results. Mostly apparent for plots with logarithmic scale:

Colormap 2D for a scalar - Matlab

i'm simulating a wave propogation in time and place. i want to make a colormap of its values for every time step, in space. i mean, i want to make a figure of 2 axes (x and y) and displays the wave's values at those points by color (the wave varible is V).
how can i do it?
i'v tried:
for ind1 = 1:length(t)
figure()
trisurf(x1,y1,V(:,ind1),'EdgeColor', 'None', 'facecolor', 'interp');
view(2);
end
but i got a message that z (=V) suppose to be a function and not a scalar.
any suggestions?
I have two options, I don't think they will be perfect, but it might help.
First, interpolate the data onto a rectangular mesh and use contourf:
F=scatteredInterp(x,y,V(:,ind1));
X=linspace(min(x),max(x));
Y=linspace(min(y),max(y));
contourf(X,Y,F(X,Y))
Secondly, use scatter to plot points with varying colour:
scatter(x,y,25,V(:,ind1))
where the 25 controls the size of each marker, you may have to experiment with it.
Hope that gives you some ideas.
i've made a loop that finally works:
clear heart_movie
Vnorm = mat2gray(V(:,1:2000));
x1_new = x1-min(x1)+1;
y1_new = y1-min(y1)+1;
for ind1 = 1:2000
heart = zeros(max(x1_new),max(y1_new));
z = Vnorm(:,ind1);
for ind2 = 1:length(z);
heart(y1_new(ind2),x1_new(ind2))= z(ind2);
end
colormap(jet);
imagesc(flipud(heart));
end

Trying to make MATLAB's figure stop 'blinking'

So I have a simple loop in MATLAB that does the following:
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
figure(1)
plot(randn(1,100));
figure(2);
plot(randn(1,100));
end
The x and y are made up, but that is the jist of it. Anyway, when I run this code, not surprisingly, MATLAB will make two figures and plot accordingly. The problem is, I get a sort of 'blinking' between figures when I do this, and it makes the quality of seeing x and y evolve over time poorer.
I discovered a way to make one of the plots smoother like this:
figure(1);
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(randn(1,100));
drawnow
end
If I do this, then of course figure(1) will plot very smoothly showing x nicely, without figure(1) 'blinking' between plots, but now I cant show figure(2) or y!
How can I plot both those quantities on different figures (not subplots) smoothly without 'blinking'?
EDIT:
Thanks Geodesic for your answer, the solution works, however there is a subtlety that I did not think would be an issue, however it is.
1) I am unable to use 'imagesc' with this solution.
For example,
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*rand(10,100);
plot(aone,x);
drawnow;
imagesc(atwo,y);
drawnow;
end
In this case the part with imagesc(atwo, y) crashes.
Your flicker is because you're generating each figure window again and again through the loop, which is forcing the window to come to the foreground each time. Generate the figures first, attach some axes to them, and plot your data to each axis like so:
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(aone,randn(1,100));
drawnow;
imagesc(y,'Parent',atwo);
drawnow;
end
Edit: functions like plot take an axis argument directly, but imagesc does not. In this particular case you'll need to send a Property Name/Value pair in as an argument. The 'Parent' of the image generated will be our axis atwo (see above).
For p = 1, create the plots you need, using the plot command or the imagesc command. Keep the handle of the resulting graphics object by getting an output argument: for example h = plot(.... or h = imagesc(..... This will be a Handle Graphics lineseries or image object, or something else, depending on the particular plot type you create.
For p = 2:100, don't use the plotting commands directly, but instead update the relevant Data properties of the original Handle Graphics object h. For example, for a lineseries object resulting from a plot command, set its XData and YData properties to the new data. For an image object resulting from an imagesc command, set its CData property to the new image.
If necessary, call drawnow after updating to force a flush of the graphics queue.

Plotting with a time axis in Matlab using dateTick and dateNumbers

I am making a script to plot burst signal events on a pesudo-waterfall.
With x-axis being frequencyand y-axis being time I plot a line from
[event-frequency event-startTimeStamp] to [event-frequency event-endTimeStamp]
to represent each burst.
I am using the following code:
tstart = datenum(0,0,0,0,0,0);
tend = datenum(0,0,0,0,0,1);
timeInterval=tend-tstart;
xlim([0 10]);
hold on;
cla;
timeAxis = linspace(tstart, tend, 100);
set(gca,'YTick',timeAxis,'FontSize',6,'YDir','reverse');
datetick('y','HH:MM::SS.FFF','keepticks');
plot([1 1],[tstart tstart+timeInterval/4]);
plot([2 2],[tstart+timeInterval/8 tstart+timeInterval/2]);
tstart=tstart + timeInterval;
tend=tend + timeInterval;
The paragraph from the cla can be repeated to plot 'signals' in later time increments of one second. This works fine.
If I change the first two lines to the following: edit: using this value cuz it's the timestamp of the first burst
tstart = datenum(2011,6,13,15,10,40.999);
tend = tstart + datenum(0,0,0,0,0,1);
The plot looks horrible and the labels get messed up into a black mess. I can't work out why it's happening. Anybody know?
(copy-pastable code if you wanna try it out)
-Daniel
Try not showing all the tick marks, instead only a subset of them. Something along the lines:
set(gca,'YTick',timeAxis(1:10:end))
Also read up on DATETICK options: 'keepticks' and 'keeplimits'