张家口市住房和城乡建设局网站,网站建设入门 下载,网站开发 制作,广州 电商网站建设一文搞定#xff1a;批量把中文头信息的 CSV#xff08;含“序号/采样频率”等#xff09;稳健转成 .mat#xff0c;并按规则重命名#xff08;H/I/O/F-rpm-fs-load#xff09;1. 项目背景
在振动/故障诊断采集里#xff0c;我们经常得到一批 CSV 文件#xff0c;文件名…一文搞定批量把中文头信息的 CSV含“序号/采样频率”等稳健转成 .mat并按规则重命名H/I/O/F-rpm-fs-load1. 项目背景
在振动/故障诊断采集里我们经常得到一批 CSV 文件文件名形如
jiankang-100rpm-2kHz-0%Load_all.csv
neiquan-1300rpm-2kHz-0%Load_all.csv
waiquan-500rpm-8kHz-10%Load_all.csv每个文件前面是若干中文元信息比如“采样频率,2.000kHz”接着是表头行通常包含**“序号”再往下是多列数据**。但是这些 CSV 在编码、分隔符、列类型上并不统一GBK/UTF-8、英文/中文逗号、文本列混入等直接 readtable 往往会报错或读乱。
本文给出一套鲁棒的 MATLAB 脚本把这类 CSV 批量转换为 .mat并把关键信息采样频率、转速、负载、列名、时间轴等保存到结构体里最后按规则重命名输出文件。2. 明确需求已全部实现
批量读取目录下的 CSV/XLSX含中文头信息、表头“序号”、多列数据。采样频率以文件内部为准优先读取“采样频率,2.000kHz”找不到再回退文件名中的 2kHz/8kHz。仅保留数值数据列遇到文本列自动尝试转数字全 NaN 的列丢弃若存在“序号”列从数据中自动剔除。按固定换算对每列做统一变换/1000 *100 *60 /4即 ×0.15。输出 .mat 顶层变量名为 data结构体字段
signal (N×C) 换算后的信号矩阵varNames (C×1 string) 列名N,C 样本数/通道数fs 采样频率(Hz)rpm 转速(r/min)load_pct 负载(%)若只有 HP 也会尝试转读缺失时置 0t 时间轴秒按 fs 自动生成
重命名规则来自实际需求
条件码来自文件名中第一段
jiankang/健康 → Hguzhang/故障 → Fneiqian/neiquan/内圈/内环 → Iwaiquan/外圈/外环 → O未识别 → X
文件名格式条件码-rpm-fs_kHz-load.mat
例如jiankang-100rpm-2kHz-0%Load → H-100-2-0.mat强鲁棒性自动尝试编码UTF-8/GB18030/ISO-8859-1、自动识别分隔符英文/中文逗号、分号、Tab、保留原始列名消除“列名被修改”警告、兼容旧版 WhitespaceRule 选项。3. 解析思路
定位数据起始优先寻找包含“序号”的表头行若找不到按“第一行像数据数字分隔符数字”来定位上一行视为表头。读表策略detectImportOptions readtable强制 VariableNamingRulepreserve保留中文列名遇到老版本不支持的选项自动跳过。列类型清洗对每列做统一转换数字列直取文本/元胞/类别列转字符串后 str2double全 NaN 的列剔除。剔除“序号”列只把它用作定位不进入最终 signal。元信息解析把表头之前的“Key,Value”行转成 KV 表优先从中解析“采样频率”kHz/Hz 均兼容。文件名解析提取条件/转速/采样率/负载HP 或 %Load。统一换算signal data * 0.15。时间轴若 fs 有效t (0:N-1)/fs。4. 完整代码保存为 batch_csv2mat.m直接把下面整段保存为 batch_csv2mat.m。MATLAB 路径切到该文件所在目录后调用即可。function batch_csv2mat(inDir, outDir)
% 批量把类似
% jiankang-100rpm-2kHz-0%Load_all.csv
% neiquan-1300rpm-2kHz-0%Load_all.csv
% jiankang-variable-speed-2kHz-0%Load_all.csv
% waiquan-variable-speed-2kHz-0%Load_all-1.csv -- 重复测试1
% 转为 .mat
%
% 命名
% 定速 code-rpm-fs_kHz-load[_RPT_n].mat
% 变速 code-VS-fs_kHz-load[_RPT_n].mat
% 例 H-100-2-0.mat, H-VS-2-0_RPT_1.mat
%
% 顶层变量名datastruct仅包含
% data.signal [N x C] 已做统一换算 ×0.15
% data.varNames [C x 1] 列名保留中文
% data.N, data.C
% data.fs (Hz), data.rpm (r/min), data.load_pct (%)
% data.t [N x 1] 时间秒fs 有效时生成if nargin 2 || isempty(outDir), outDir fullfile(inDir,mat); endif ~exist(inDir,dir), error(输入目录不存在%s, inDir); endif ~exist(outDir,dir), mkdir(outDir); endfiles [dir(fullfile(inDir,*.csv)); dir(fullfile(inDir,*.CSV)); ...dir(fullfile(inDir,*.txt)); dir(fullfile(inDir,*.TXT)); ...dir(fullfile(inDir,*.xlsx)); dir(fullfile(inDir,*.XLSX))];if isempty(files)warning(目录中未发现 CSV/TXT/XLSX%s, inDir);return;endwarning(off,MATLAB:table:ModifiedVarnames);warning(off,MATLAB:table:ModifiedAndSavedVarnames);for k 1:numel(files)fpath fullfile(files(k).folder, files(k).name);tryrecFull parse_one_file(fpath); % 解析含 is_vs / rpt_idxoutname make_outname(recFull, files(k).name); % 生成目标 .mat 名data prune_and_convert(recFull); % 仅保留 换算 生成 tsave(fullfile(outDir,outname), data, -v7.3); % 顶层变量名为 datafprintf(OK - %s\n, outname);catch MEfprintf(2,FAIL - %s\n %s\n, files(k).name, ME.message);endend
end%% 仅保留字段并做换算并生成 t
function data prune_and_convert(R)scale (10*60)/(4*1000); % /1000 * 10 * 60 / 4 0.15signal R.data * scale;data struct();data.signal signal; % [N x C]data.varNames R.varNames; % 列名data.N size(signal,1);data.C size(signal,2);data.fs R.fs;data.rpm R.rpm;data.load_pct R.load_pct;if ~isnan(R.fs) R.fs 0data.t (0:data.N-1). / R.fs;elsedata.t [];end
end%% 单文件解析鲁棒支持 VS / RPT仅提数值列
function rec parse_one_file(fpath)[~, base, ext] fileparts(fpath);isCSV ismember(lower(ext), {.csv,.txt});% ---------- 0) 提取重复测试编号并得到用于解析的 baseCore ----------% 支持xxx_all-1, xxx-1结尾为 -数字rpt_idx NaN;tok regexp(base, (?:_all)?-(\d)$, tokens, once);if ~isempty(tok), rpt_idx str2double(tok{1}); endbaseCore regexprep(base, (?:_all)?-(\d)$, ); % 去掉尾部编号% ---------- 1) 多编码读取并清洗仅 CSV/TXT ----------lines strings(0,1); encList {UTF-8,GB18030,ISO-8859-1};if isCSVfor e 1:numel(encList)trylines readlines(fpath, Encoding, encList{e});if ~isempty(lines); break; endcatch, endendif isempty(lines), error(无法按常见编码读取此文件); endlines normalize_lines(lines);end% ---------- 2) 表头行定位 ----------headerLineIdx [];if isCSVheaderLineIdx find(contains(lines, 序号), 1, first);if isempty(headerLineIdx)pat ^\s*\d\s*[,;\t]\s*[-\d\.];isData ~cellfun(isempty, regexp(cellstr(lines), pat, once));dataStart find(isData, 1, first);if ~isempty(dataStart) dataStart 1headerLineIdx dataStart - 1;elseerror(未找到“序号”表头且无法定位数据起始行。);endendend% ---------- 3) detectImportOptions readtable ----------if isCSVopts detectImportOptions(fpath, NumHeaderLines, headerLineIdx-1);if isempty(opts.Delimiter) || isequal(opts.Delimiter, )opts.Delimiter {,,;,\t,};endtry, opts.VariableNamingRule preserve; catch, endtry, opts.PreserveVariableNames true; catch, endtrytxtVars opts.VariableNames( ismember(opts.VariableTypes, {char,string,categorical}) );if ~isempty(txtVars)try, opts setvaropts(opts, txtVars, WhitespaceRule,preserve); catch, endtry, opts setvaropts(opts, txtVars, EmptyFieldRule,auto); catch, endendcatch, endtryT readtable(fpath, opts);catchtry, T readtable(fpath, VariableNamingRule,preserve); catchT readtable(fpath, PreserveVariableNames, true);endendelsetry, T readtable(fpath, VariableNamingRule,preserve); catchT readtable(fpath, PreserveVariableNames, true);endendif isempty(T), error(表格为空%s, fpath); end% ---------- 4) 仅提取数值列自动数值化剔除全 NaN / 不齐列 ----------[A, vnames] table_to_numeric(T);if isempty(A) || size(A,2) 0error(未能从表格中提取到任何数值列%s, fpath);end% 若存在“序号”从数据中移除idxCol find(contains(vnames, 序号), 1, first);if ~isempty(idxCol)data A(:, setdiff(1:size(A,2), idxCol));varNames vnames(setdiff(1:numel(vnames), idxCol));elsedata A;varNames vnames;end% ---------- 5) 元信息 采样频率内部优先 ----------meta struct(); meta.raw strings(0,1);if isCSV headerLineIdx1, meta.raw lines(1:headerLineIdx-1); endmeta.kv table(string.empty, string.empty,VariableNames,{Key,Value});if ~isempty(meta.raw)K strings(0,1); V strings(0,1);for i 1:numel(meta.raw)s char(meta.raw(i)); if isempty(s), continue; ends strrep(s,,,); s strrep(s,,;);parts split(string(s), ,);if numel(parts)2K(end1,1) strtrim(parts(1));V(end1,1) strtrim(strjoin(parts(2:end), ,)); %#okAGROWendendmeta.kv table(K, V, VariableNames, {Key,Value});endfs NaN;if ~isempty(meta.kv.Key)hit contains(meta.kv.Key, 采样频率);if any(hit)val meta.kv.Value(find(hit,1,first));tok regexp(val, ([\d\.])\s*([kK]?[Hh]z)?, tokens, once);if ~isempty(tok)v str2double(tok{1}); unit lower(strtrim(tok{2}));if isempty(unit)||strcmp(unit,hz), fsv;elseif strcmp(unit,khz), fsv*1000;else, fsv;endendendend% ---------- 6) 文件名解析定速 or 变速 VS ----------cond; rpmNaN; fs_nameNaN; load_pctNaN; load_hpNaN; is_vsfalse;% 定速xxx-1000rpm-2kHz-0%Load / xxx-1000rpm-2kHz-10HPm1 regexp(baseCore,^(?cond[^-])-(?rpm\d)rpm-(?fs[\d\.])[kK]Hz-(?hp\d)HP,names);m2 regexp(baseCore,^(?cond[^-])-(?rpm\d)rpm-(?fs[\d\.])[kK]Hz-(?pct\d)\%Load,names);% 变速xxx-variable-speed-2kHz-0%Load / xxx-vs-2kHz-...vsToken (?:variable[-_ ]?speed|variablespeed|vs|bianzhuansu|bian_su|bian_zs|变转速|变速|变转);mVS1 regexp(baseCore, [^(?cond[^-])- vsToken -(?fs[\d\.])[kK]Hz-(?hp\d)HP], names);mVS2 regexp(baseCore, [^(?cond[^-])- vsToken -(?fs[\d\.])[kK]Hz-(?pct\d)\%Load], names);if ~isempty(m1)condstring(m1.cond); rpmstr2double(m1.rpm);fs_namestr2double(m1.fs)*1000; load_hpstr2double(m1.hp);elseif ~isempty(m2)condstring(m2.cond); rpmstr2double(m2.rpm);fs_namestr2double(m2.fs)*1000; load_pctstr2double(m2.pct);elseif ~isempty(mVS1)condstring(mVS1.cond); is_vstrue;fs_namestr2double(mVS1.fs)*1000; load_hpstr2double(mVS1.hp);elseif ~isempty(mVS2)condstring(mVS2.cond); is_vstrue;fs_namestr2double(mVS2.fs)*1000; load_pctstr2double(mVS2.pct);endif isnan(fs), fs fs_name; end% ---------- 7) 输出供命名与裁剪使用 ----------rec struct();rec.data data;rec.varNames varNames;rec.fs fs;rec.rpm rpm;rec.load_pct load_pct;rec.load_hp load_hp;rec.condition cond;rec.is_vs is_vs; % 是否变速rec.rpt_idx rpt_idx; % 重复测试编号NaN 表示无编号
end%% 把 table 列转成纯数值
function [A, vnames] table_to_numeric(T)V T.Properties.VariableNames;n height(T);cols []; vnames strings(0,1);for i 1:numel(V)x T.(V{i});if isrow(x), x x.; endif isnumeric(x)num double(x);elseif islogical(x)num double(x);elseif iscell(x) || isstring(x) || ischar(x) || iscategorical(x)if iscategorical(x), x cellstr(x); endif iscell(x)trys string(x);catchs string(cellfun((z)string(z), x, UniformOutput, false));endelses string(x);ends strrep(s, , );s strrep(s, ,, ); % 千分位逗号num str2double(s);elsecontinue; % 其它类型不处理endif ~isnumeric(num) || all(isnan(num)) || numel(num)~ncontinue; % 丢掉全 NaN 或长度不匹配的列endcols [cols, num]; %#okAGROWvnames(end1,1) string(V{i}); %#okAGROWendA cols;
end%% 命名定速/变速 重复测试后缀
function outname make_outname(rec, origName)% 条件码映射H(健康) F(故障) I(内圈) O(外圈)condMap containers.Map( ...{ jiankang,healthy,health,jk,normal,健康, ...guzhang,fault,gz,faulty,故障, ...neiqian,neiquan,inner,nei,内圈,内环,内, ...waiquan,outer,wai,外圈,外环,外 }, ...{ H,H,H,H,H,H, ...F,F,F,F,F, ...I,I,I,I,I,I,I, ...O,O,O,O,O,O } );key lower(string(rec.condition));if condMap.isKey(key)code condMap(key);elseif strlength(key)0code upper(extractBefore(key , 2));elsecode X;endfs_khz rec.fs/1000; if isnan(fs_khz), fs_khz 0; endfs_khz round(fs_khz);if ~isnan(rec.load_hp)loadVal rec.load_hp;elseif ~isnan(rec.load_pct)loadVal rec.load_pct;elseloadVal 0;endloadVal round(loadVal);if isfield(rec,is_vs) rec.is_vsbaseName sprintf(%s-VS-%d-%d, code, fs_khz, loadVal);elserpm rec.rpm; if isnan(rpm), rpm 0; endbaseName sprintf(%s-%d-%d-%d, code, round(rpm), fs_khz, loadVal);end% 重复测试编号后缀if isfield(rec,rpt_idx) ~isnan(rec.rpt_idx)baseName sprintf(%s_RPT_%d, baseName, rec.rpt_idx);endoutname [baseName .mat];if codeXwarning(无法从条件解析出代码%s - 用 X 代替文件%s, string(rec.condition), origName);end
end%% 工具规范行文本
function lines normalize_lines(lines)lines replace(lines, char(65279), ); % BOMlines replace(lines, , ,);lines replace(lines, , ;);
end5. 使用方法
% 1) 放置
% 将 batch_csv2mat.m 放到 MATLAB 当前工作目录% 2) 执行输出目录可省略默认在输入目录下新建 mat/
batch_csv2mat(F:\input, ...F:\mat_out);% 3) 查看一个转换结果
S load(F:\2025.9.6-欧瑞-6305-轴承\mat_out\H-100-2-0.mat); % 举例
data S.data;
plot(data.t, data.signal(:,1)); grid on
xlabel(Time (s)); ylabel(data.varNames(1));
title(Channel 1);6. 输出内容说明
MAT 文件名H/I/O/F-rpm-fs_kHz-load.matMAT 内部变量顶层变量 datastruct
signalN×C已统一换算×0.15tN×1 秒fs/rpm/load_pct数值信息varNames列名中文保留N/C样本数/通道数7. 常见问题与已处理
“变量名被修改”警告 → 已强制保留原始列名VariableNamingRulepreserve并静默相关警告。WhitespaceRule 未知 → 仅在文本列存在且当前版本支持时才设置不支持自动跳过。“无法串联 double 和 cell” → 读取后对每列做数值化全 NaN 列剔除彻底避免此类报错。找不到“序号”表头 → 启用“数据模式”兜底定位第一行“像数据”的行自动确定表头。编码/分隔符混乱 → 自动尝试 UTF-8/GB18030/ISO-8859-1分隔符支持英文/中文逗号、分号、Tab。8. 一致性校验可选
想确认换算与数据无丢失可做如下对比把 CSV 原始数值×0.15 后与 .mat 比较
csvf ...原CSV路径...;
S load(...对应的.mat);
data S.data;T readtable(csvf,VariableNamingRule,preserve);
A table2array(T); % [序号, 数据...]
X A(:,2:end) * 0.15; % 同步换算fprintf(max abs diff %.3g\n, max(abs(X(:)-data.signal(:))));max abs diff 应该接近 0浮点微小误差内。9. 可扩展方向
并行加速外层 for 可改 parfor需要 Parallel Toolbox。统一合并把所有 MAT 聚合成一个大矩阵 索引表condition/rpm/fs/load。自定义换算把 scale 0.15 改成配置项或为不同列设置不同系数。更丰富的命名映射在 condMap 内继续扩展你的条件类别。10. 结语
这套脚本针对“中文头信息 序号表头 多源编码/分隔符 列类型不一致”的工业 CSV 做了较强的兼容性处理并把研究中常用的关键信息全部沉淀进 .mat变量名、采样率、时间轴等开箱即用。欢迎在此基础上继续定制比如统一单位、自动频谱、批量可视化、或者和后续深度学习数据管线打通等。