Added a function to export figure data into ascii txt files (and to plot ascii text files to check if export worked)
This commit is contained in:
1 parent
5dcb0a9119
commit
484648b648
2 files changed
+432
No files matched your search
@@ -0,0 +1,281 @@
|
||||
function exportFigureData(figHandle)
|
||||
%exportFigureData Export line and scatter plot data to ASCII files
|
||||
%
|
||||
% This function extracts x and y data from all line and scatter plots in a
|
||||
% figure (including tiled layouts) and saves each data series to a separate
|
||||
% ASCII file. File names are constructed from the figure title, tile title
|
||||
% (if in a tiled layout), axis labels, and legend entries. Data is limited
|
||||
% to the current x-axis scale range.
|
||||
%
|
||||
% SYNTAX:
|
||||
% exportFigureData() % Uses current active figure
|
||||
% exportFigureData(figHandle) % Uses specified figure handle
|
||||
%
|
||||
% INPUTS:
|
||||
% figHandle : figure handle (optional)
|
||||
% If empty or omitted, the current active figure (gcf) is used.
|
||||
%
|
||||
% OUTPUT:
|
||||
% ASCII files are saved in the current working directory, named as:
|
||||
% <FigureTitle>_<TileTitle>_<YLabel>_vs_<XLabel>_<LineLabel>.txt
|
||||
% For non-tiled figures:
|
||||
% <FigureTitle>_<YLabel>_vs_<XLabel>_<LineLabel>.txt
|
||||
% Each file contains two columns: X-data and Y-data (tab-delimited)
|
||||
%
|
||||
% NOTES:
|
||||
% - Works with line plots (line objects) and scatter plots (scatter objects)
|
||||
% - Handles both regular axes and tiled layouts
|
||||
% - Data outside the x-axis scale limits are excluded
|
||||
% - Lines without legend entries are named as 'line_N' where N is a counter
|
||||
% - Scatter plots without legend entries are named as 'scatter_N'
|
||||
% - Axis labels default to 'x' and 'y' if not set
|
||||
% - Tile titles are included only for tiled layouts
|
||||
% - If no figure title is set, 'figure' is used as the base name
|
||||
%
|
||||
% EXAMPLE:
|
||||
% % Create a tiled figure with multiple subplots
|
||||
% figure
|
||||
% tiledlayout(2, 1, 'TileSpacing', 'compact')
|
||||
%
|
||||
% nexttile
|
||||
% x = 0:0.1:10;
|
||||
% plot(x, sin(x), 'DisplayName', 'sin(x)')
|
||||
% hold on
|
||||
% plot(x, cos(x), 'DisplayName', 'cos(x)')
|
||||
% title('Sine and Cosine')
|
||||
% xlabel('Time (s)')
|
||||
% ylabel('Amplitude')
|
||||
% legend
|
||||
%
|
||||
% nexttile
|
||||
% plot(x, sin(x).*cos(x), 'DisplayName', 'sin*cos')
|
||||
% title('Product')
|
||||
% xlabel('Time (s)')
|
||||
% ylabel('Value')
|
||||
% legend
|
||||
%
|
||||
% sgtitle('Trigonometric Functions')
|
||||
% exportFigureData(gcf)
|
||||
%
|
||||
% % Creates files like:
|
||||
% % Trigonometric_Functions_Sine_and_Cosine_Amplitude_vs_Time_(s)_sin(x).txt
|
||||
% % Trigonometric_Functions_Sine_and_Cosine_Amplitude_vs_Time_(s)_cos(x).txt
|
||||
% % Trigonometric_Functions_Product_Value_vs_Time_(s)_sin*cos.txt
|
||||
%
|
||||
% See also: tiledlayout, nexttile, legend, line, scatter
|
||||
|
||||
% Handle input: use current figure if none provided
|
||||
if nargin == 0 || isempty(figHandle)
|
||||
figHandle = gcf;
|
||||
end
|
||||
|
||||
% Get figure title (use sgtitle if available, else Name)
|
||||
figTitle = '';
|
||||
sgtitleObjs = findall(figHandle, 'type', 'text', 'Tag', 'sgtitle');
|
||||
if ~isempty(sgtitleObjs)
|
||||
figTitle = get(sgtitleObjs(1), 'String');
|
||||
if iscell(figTitle)
|
||||
figTitle = figTitle{1};
|
||||
end
|
||||
end
|
||||
if isempty(figTitle)
|
||||
figTitle = get(figHandle, 'Name');
|
||||
end
|
||||
if isempty(figTitle)
|
||||
figTitle = 'figure';
|
||||
end
|
||||
figTitle = sanitizeFilename(figTitle); % Sanitize figure title
|
||||
|
||||
% Get all axes in the figure (works for both regular and tiled layouts)
|
||||
allAxes = findall(figHandle, 'type', 'axes');
|
||||
if isempty(allAxes)
|
||||
warning('exportFigureData:NoAxes', 'No axes found in figure.');
|
||||
return;
|
||||
end
|
||||
|
||||
% Counters for unnamed line and scatter objects
|
||||
lineCounter = 0;
|
||||
scatterCounter = 0;
|
||||
|
||||
% Loop over all axes
|
||||
for ii = 1:length(allAxes)
|
||||
ax = allAxes(ii);
|
||||
|
||||
% Skip legend axes and colorbar axes
|
||||
if strcmp(ax.Tag, 'legend') || strcmp(ax.Tag, 'colorbar')
|
||||
continue
|
||||
end
|
||||
|
||||
% Get axis titles
|
||||
xLabel = ax.XLabel.String;
|
||||
yLabel = ax.YLabel.String;
|
||||
tileTitle = ax.Title.String;
|
||||
|
||||
% Convert to cell if char, then handle empty cases
|
||||
if ischar(xLabel), xLabel = cellstr(xLabel); end
|
||||
if ischar(yLabel), yLabel = cellstr(yLabel); end
|
||||
if ischar(tileTitle), tileTitle = cellstr(tileTitle); end
|
||||
|
||||
if isempty(xLabel) || (iscell(xLabel) && isempty(xLabel{1}))
|
||||
xLabel = 'x';
|
||||
else
|
||||
xLabel = xLabel{1};
|
||||
end
|
||||
|
||||
if isempty(yLabel) || (iscell(yLabel) && isempty(yLabel{1}))
|
||||
yLabel = 'y';
|
||||
else
|
||||
yLabel = yLabel{1};
|
||||
end
|
||||
|
||||
if isempty(tileTitle) || (iscell(tileTitle) && isempty(tileTitle{1}))
|
||||
tileTitle = '';
|
||||
else
|
||||
tileTitle = tileTitle{1};
|
||||
end
|
||||
|
||||
% Sanitize axis labels and tile title
|
||||
xLabel = sanitizeFilename(xLabel);
|
||||
yLabel = sanitizeFilename(yLabel);
|
||||
tileTitle = sanitizeFilename(tileTitle);
|
||||
|
||||
% Get x-axis limits for filtering
|
||||
xLims = ax.XLim;
|
||||
|
||||
% Get all children of the axes (lines, scatter, etc.)
|
||||
children = get(ax, 'Children');
|
||||
|
||||
% Build a legend object if it exists (for extracting labels)
|
||||
legendObj = legend(ax);
|
||||
legendLabels = {};
|
||||
if ~isempty(legendObj)
|
||||
legendLabels = legendObj.String;
|
||||
if ischar(legendLabels)
|
||||
legendLabels = {legendLabels};
|
||||
end
|
||||
end
|
||||
|
||||
% Initialize a counter for matching legend entries to graphics objects
|
||||
graphicsIndex = 0;
|
||||
|
||||
% Loop over all child graphics objects
|
||||
for jj = 1:length(children)
|
||||
obj = children(jj);
|
||||
|
||||
% Check if this is a line object
|
||||
if isa(obj, 'matlab.graphics.chart.primitive.Line')
|
||||
graphicsIndex = graphicsIndex + 1;
|
||||
|
||||
% Get x and y data
|
||||
xData = get(obj, 'XData');
|
||||
yData = get(obj, 'YData');
|
||||
|
||||
% Skip if empty
|
||||
if isempty(xData) || isempty(yData)
|
||||
continue
|
||||
end
|
||||
|
||||
% Filter data to x-axis range
|
||||
mask = (xData >= xLims(1)) & (xData <= xLims(2));
|
||||
xData = xData(mask);
|
||||
yData = yData(mask);
|
||||
|
||||
% Determine label: use legend entry or auto-generate name
|
||||
if graphicsIndex <= length(legendLabels) && ~isempty(legendLabels{graphicsIndex})
|
||||
label = legendLabels{graphicsIndex};
|
||||
else
|
||||
lineCounter = lineCounter + 1;
|
||||
label = sprintf('line_%d', lineCounter);
|
||||
end
|
||||
|
||||
% Sanitize label for filename
|
||||
label = sanitizeFilename(label);
|
||||
|
||||
% Create filename with tile title if present
|
||||
if ~isempty(tileTitle)
|
||||
fileName = sprintf('%s_%s_%s_vs_%s_%s.txt', figTitle, tileTitle, yLabel, xLabel, label);
|
||||
else
|
||||
fileName = sprintf('%s_%s_vs_%s_%s.txt', figTitle, yLabel, xLabel, label);
|
||||
end
|
||||
|
||||
% Write data to ASCII file
|
||||
data = [xData(:), yData(:)];
|
||||
writematrix(data, fileName, 'Delimiter', '\t');
|
||||
fprintf('Saved: %s\n', fileName);
|
||||
|
||||
% Check if this is a scatter object
|
||||
elseif isa(obj, 'matlab.graphics.chart.primitive.Scatter')
|
||||
graphicsIndex = graphicsIndex + 1;
|
||||
|
||||
% Get x and y data
|
||||
xData = get(obj, 'XData');
|
||||
yData = get(obj, 'YData');
|
||||
|
||||
% Skip if empty
|
||||
if isempty(xData) || isempty(yData)
|
||||
continue
|
||||
end
|
||||
|
||||
% Ensure row vectors for indexing
|
||||
xData = xData(:)';
|
||||
yData = yData(:)';
|
||||
|
||||
% Filter data to x-axis range
|
||||
mask = (xData >= xLims(1)) & (xData <= xLims(2));
|
||||
xData = xData(mask);
|
||||
yData = yData(mask);
|
||||
|
||||
% Determine label: use legend entry or auto-generate name
|
||||
if graphicsIndex <= length(legendLabels) && ~isempty(legendLabels{graphicsIndex})
|
||||
label = legendLabels{graphicsIndex};
|
||||
else
|
||||
scatterCounter = scatterCounter + 1;
|
||||
label = sprintf('scatter_%d', scatterCounter);
|
||||
end
|
||||
|
||||
% Sanitize label for filename
|
||||
label = sanitizeFilename(label);
|
||||
|
||||
% Create filename with tile title if present
|
||||
if ~isempty(tileTitle)
|
||||
fileName = sprintf('%s_%s_%s_vs_%s_%s.txt', figTitle, tileTitle, yLabel, xLabel, label);
|
||||
else
|
||||
fileName = sprintf('%s_%s_vs_%s_%s.txt', figTitle, yLabel, xLabel, label);
|
||||
end
|
||||
|
||||
% Write data to ASCII file
|
||||
data = [xData(:), yData(:)];
|
||||
writematrix(data, fileName, 'Delimiter', '\t');
|
||||
fprintf('Saved: %s\n', fileName);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fprintf('Data export complete.\n');
|
||||
end
|
||||
|
||||
% =========================================================================
|
||||
% Helper function: sanitize strings for use in filenames
|
||||
% =========================================================================
|
||||
function cleanStr = sanitizeFilename(str)
|
||||
%sanitizeFilename Remove or replace characters invalid in filenames
|
||||
%
|
||||
% Replaces spaces, slashes, backslashes, colons, asterisks, parentheses,
|
||||
% and other problematic characters with underscores.
|
||||
|
||||
cleanStr = str;
|
||||
cleanStr = strrep(cleanStr, ' ', '_');
|
||||
cleanStr = strrep(cleanStr, '/', '_');
|
||||
cleanStr = strrep(cleanStr, '\', '_');
|
||||
cleanStr = strrep(cleanStr, ':', '_');
|
||||
cleanStr = strrep(cleanStr, '*', '_');
|
||||
cleanStr = strrep(cleanStr, '?', '_');
|
||||
cleanStr = strrep(cleanStr, '"', '_');
|
||||
cleanStr = strrep(cleanStr, '<', '_');
|
||||
cleanStr = strrep(cleanStr, '>', '_');
|
||||
cleanStr = strrep(cleanStr, '|', '_');
|
||||
cleanStr = strrep(cleanStr, '(', '_');
|
||||
cleanStr = strrep(cleanStr, ')', '_');
|
||||
cleanStr = strrep(cleanStr, '.', '_'); % Optional: replace dots
|
||||
|
||||
end
|
||||
@@ -0,0 +1,151 @@
|
||||
function plotExportedData(dataDirectory)
|
||||
%plotExportedData Plot all exported ASCII data files in one figure
|
||||
%
|
||||
% This function reads all .txt files from the exported data (created by
|
||||
% exportFigureData) and plots them in a single figure for visual comparison.
|
||||
% File names are parsed to extract axis labels and legend entries.
|
||||
%
|
||||
% SYNTAX:
|
||||
% plotExportedData() % Uses current working directory
|
||||
% plotExportedData(dataDirectory) % Uses specified directory path
|
||||
%
|
||||
% INPUTS:
|
||||
% dataDirectory : char or string (optional)
|
||||
% Path to directory containing exported .txt files.
|
||||
% If omitted, uses current working directory (pwd).
|
||||
%
|
||||
% OUTPUT:
|
||||
% A new figure with all data plotted as lines. Axis labels and legend
|
||||
% entries are reconstructed from the file names.
|
||||
%
|
||||
% NOTES:
|
||||
% - Reads all .txt files in the specified directory
|
||||
% - File name format expected: <FigureTitle>_[<TileTitle>_]<YLabel>_vs_<XLabel>_<LineLabel>.txt
|
||||
% - Plots are created with underscores converted back to spaces for display
|
||||
% - X and Y axis labels are set based on parsed filename
|
||||
% - Legend shows line labels from filenames
|
||||
% - All data is plotted in a single axes for direct visual comparison
|
||||
%
|
||||
% EXAMPLE:
|
||||
% % After running exportFigureData():
|
||||
% plotExportedData()
|
||||
% % or
|
||||
% plotExportedData('../exported_data/')
|
||||
|
||||
% Handle input: use current directory if none provided
|
||||
if nargin == 0 || isempty(dataDirectory)
|
||||
dataDirectory = pwd;
|
||||
end
|
||||
|
||||
% Get all .txt files in the directory
|
||||
fileStruct = dir(fullfile(dataDirectory, '*.txt'));
|
||||
if isempty(fileStruct)
|
||||
warning('plotExportedData:NoFiles', 'No .txt files found in %s', dataDirectory);
|
||||
return;
|
||||
end
|
||||
|
||||
% Create a new figure
|
||||
fig = figure('Name', 'Exported Data Comparison', 'NumberTitle', 'off');
|
||||
ax = axes(fig);
|
||||
hold(ax, 'on');
|
||||
grid(ax, 'on');
|
||||
|
||||
% Storage for axis labels (to ensure consistency)
|
||||
xLabelStr = '';
|
||||
yLabelStr = '';
|
||||
legendEntries = {};
|
||||
|
||||
% Loop over all .txt files
|
||||
for ii = 1:length(fileStruct)
|
||||
fileName = fileStruct(ii).name;
|
||||
filePath = fullfile(dataDirectory, fileName);
|
||||
|
||||
% Read the data
|
||||
try
|
||||
data = readmatrix(filePath);
|
||||
catch ME
|
||||
warning('plotExportedData:ReadFailed', 'Could not read file: %s\n%s', fileName, ME.message);
|
||||
continue;
|
||||
end
|
||||
|
||||
% Check if data has at least 2 columns
|
||||
if size(data, 2) < 2 || size(data, 1) < 1
|
||||
warning('plotExportedData:InvalidData', 'File %s does not have valid X,Y columns', fileName);
|
||||
continue;
|
||||
end
|
||||
|
||||
xData = data(:, 1);
|
||||
yData = data(:, 2);
|
||||
|
||||
% Parse filename to extract labels
|
||||
% Expected format: <FigureTitle>_[<TileTitle>_]<YLabel>_vs_<XLabel>_<LineLabel>.txt
|
||||
|
||||
% Remove .txt extension
|
||||
nameNoExt = fileName(1:end-4);
|
||||
|
||||
% Split by '_vs_' to separate y-axis label from the rest
|
||||
parts = strsplit(nameNoExt, '_vs_');
|
||||
|
||||
if length(parts) >= 2
|
||||
% Extract line label (after _vs_<XLabel>_)
|
||||
rightPart = parts{2}; % <XLabel>_<LineLabel>
|
||||
|
||||
% Find the last underscore to separate XLabel from LineLabel
|
||||
underscores = find(rightPart == '_');
|
||||
if ~isempty(underscores)
|
||||
lastUnderscore = underscores(end);
|
||||
xLabelExtracted = rightPart(1:lastUnderscore-1);
|
||||
lineLabel = rightPart(lastUnderscore+1:end);
|
||||
else
|
||||
xLabelExtracted = rightPart;
|
||||
lineLabel = 'data';
|
||||
end
|
||||
|
||||
% Extract y-axis label (before _vs_)
|
||||
yLabelExtracted = parts{1};
|
||||
|
||||
% Remove figure title (first part before first underscore)
|
||||
% This is tricky, so we'll keep what we have and note it's approximate
|
||||
|
||||
% Convert underscores back to spaces for display
|
||||
xLabelDisplay = strrep(xLabelExtracted, '_', ' ');
|
||||
yLabelDisplay = strrep(yLabelExtracted, '_', ' ');
|
||||
lineLabel = strrep(lineLabel, '_', ' ');
|
||||
|
||||
% Store axis labels (use the first valid ones found)
|
||||
if isempty(xLabelStr)
|
||||
xLabelStr = xLabelDisplay;
|
||||
end
|
||||
if isempty(yLabelStr)
|
||||
yLabelStr = yLabelDisplay;
|
||||
end
|
||||
|
||||
else
|
||||
% Fallback if filename parsing fails
|
||||
lineLabel = nameNoExt;
|
||||
end
|
||||
|
||||
% Plot the data
|
||||
plot(ax, xData, yData, 'DisplayName', lineLabel, 'LineWidth', 1.5, 'Marker', 'o', 'MarkerSize', 4);
|
||||
|
||||
% Store legend entry
|
||||
legendEntries{end+1} = lineLabel;
|
||||
end
|
||||
|
||||
% Set axis labels
|
||||
xlabel(ax, xLabelStr, 'Interpreter', 'none');
|
||||
ylabel(ax, yLabelStr, 'Interpreter', 'none');
|
||||
title(ax, 'Exported Data Comparison', 'Interpreter', 'none');
|
||||
|
||||
% Add legend
|
||||
if ~isempty(legendEntries)
|
||||
legend(ax, legendEntries, 'Interpreter', 'none', 'Location', 'best');
|
||||
end
|
||||
|
||||
% Optimize figure appearance
|
||||
grid(ax, 'on');
|
||||
hold(ax, 'off');
|
||||
|
||||
fprintf('Plotted %d data files from: %s\n', length(fileStruct), dataDirectory);
|
||||
|
||||
end
|
||||
Reference in new issue
Block a user