前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MATLAB读取56万行地震目录只需1秒

MATLAB读取56万行地震目录只需1秒

原创
作者头像
传愿
修改2020-03-30 11:37:52
2.3K1
修改2020-03-30 11:37:52
举报
文章被收录于专栏:用户6451966的专栏

简介


现有软件读取目录,经常遇到几种情况。

  • 字符串格式不正确,要关闭软件。
  • 内存超出,要关闭软件。。
  • 进度条载入中,99%出错。。。

于是,写了个小界面。新手入门,一般酷爱循环。因为书本上一开始介绍的就是循环,函数,字符串之类的。前几章学完,就找一些实例去练习。慢慢地,认为没有什么问题是一个循环解决不了的。如果有,那就用两个循环解决。于是,嵌套,并列,判断。选择都用上了。

一开始用循环读目录,经常把MATLAB搞死。后来想了好多办法。比如,切割成小文件读,转换格式后读,等等。其实,菜鸟遇到的问题。大牛们以前都遇到过。诸多大牛大多数都被官方收编。逐渐变成了官方开发人员,推出了更多实用工具。

improt导入数据功能早在2006年之前就推出。但不知道有这个功能。最近发现可以用这个函数导入数据,并可以做前期的数据清理。比如字符串切割劈分,等宽劈分,空格等固定字符劈分功能。而且可以读取带不规则分隔符和头文件的数据信息。比如,现有软件导出的*.MIF边界文件。

实例与代码


先说说MATLAB的ui前置。类似clear函数省略为cl前缀用法。

  • cla——clear axis 清理图层
  • clf——clear figure 清理图窗
  • clc——clear command 清理命令窗口

习惯上,有些人在每一个脚本前都要用到以下命令,清理窗口,变量和图形窗口。

代码语言:matlab
复制
%% 经常使用
clc;
clear;
close all;

每次输入比较麻烦,可以用evalin函数代替。

代码语言:javascript
复制
evalin('base','clc;clear;close all')
% 也可以加其他命令
evalin('base','clc;clear;close all;Zmap') % 打开Zmap软件

交互操作函数

有点跑偏。简单来说,部分函数前加个ui表示该函数的界面交互调用方式。

  • uisetcolor 选择颜色
  • uigetdir 获取目录路径
  • uigetfile 获取文件路径
  • uiload 导入
  • uisave 保存

数据导入

简单介绍下uiimport的使用。只需MATLAB界面输入:

代码语言:javascript
复制
uiimport
选择“文件”
选择“文件”

选择“文件”,导入一个目录。

文件载入
文件载入

红色箭头标注的地方需要设置。

  • 目录是按位保存的。用等宽分割劈分列数据。
  • 范围选择全部。键盘Ctrl+A选择全部,类似Excel可以单独选择用到的有效列。
  • 输出类型。如果是纯数据选择矩阵,带汉字的可以用元胞或表格。
  • 替换。这里选默认即可。如果是MapSis导出的MIF文件,这个功能可以直接把头文件和其他信息过滤。下一节有介绍。
  • 导入所选内容。选择函数,一步生成自己用的函数。比如OpenEqt,方便以后调用函数直接读取EQT格式目录。
  • 列劈分。经度和震级需要切割。右键选择,分割列,拖动鼠标,咵跨跨,切成2列。

导出代码

三种形式利用导入的数据。一,导入工作空间。二,导出为脚本。三,导出为函数。下面是导出脚本的代码。

代码语言:matlab
复制
filename = 'D:\EqCatlog\CEIC_ML2.EQT';
formatSpec = '%15f%6f%7f%4f%6f%C%[^\n\r]';
fileID = fopen(filename,'r');
dataArray = textscan(fileID, formatSpec, 'Delimiter', '', 'WhiteSpace', ...
    '', 'TextType', 'string', 'EmptyValue', NaN,  'ReturnOnError', false);
fclose(fileID);
EqTim  = dataArray{:, 1};
EqLat  = dataArray{:, 2};
EqLon  = dataArray{:, 3};
EqMag  = dataArray{:, 4};
EqDeep = dataArray{:, 5};
EqSite = dataArray{:, 6};
clearvars filename formatSpec fileID dataArray ans;

运行时间查看,代码前加tic,代码后toc。

代码语言:javascript
复制
tic
% 上边的代码
toc

时间已过 1.060942 秒。

举一反三

同样的道理。读取MIF边界文件,效率也极高。首先通过上边的方法生成函数ReadMif.m,然后调用函数转换数据。

代码语言:javascript
复制
B180 = 'D:\Eq\mapData\areaBankuai\版块边界180.MIF';
Bk = ReadMif(B180 , 1, inf); % 路径,开始行,终止行
代码语言:javascript
复制
function D = ReadMif(filename, startRow, endRow)
%IMPORTFILE 将文本文件中的数值数据作为矩阵导入。
%   D = ReadMif(FILENAME) 读取文本文件 FILENAME 中默认选定范围的数据。
%
%   D = ReadMif(FILENAME, STARTROW, ENDROW) 读取文本文件 FILENAME 的 STARTROW
%   行到 ENDROW 行中的数据。
%
% Example:
%   D = ReadMif('版块边界180.MIF', 4, 19580);
%
%    另请参阅 TEXTSCAN。

% 由 MATLAB 自动生成于 2020/03/27 14:43:00

%% 初始化变量。
delimiter = ' ';
if nargin<=2
    startRow = 1;
    endRow = inf;
end

%% 将数据列作为文本读取:
% 有关详细信息,请参阅 TEXTSCAN 文档。
formatSpec = '%q%q%q%[^\n\r]';

%% 打开文本文件。
fileID = fopen(filename,'r');

%% 根据格式读取数据列。
% 该调用基于生成此代码所用的文件的结构。如果其他文件出现错误,请尝试通过导入工具重新生成代码。
textscan(fileID, '%[^\n\r]', startRow(1)-1, 'WhiteSpace', '', 'ReturnOnError', false);
dataArray = textscan(fileID, formatSpec, endRow(1)-startRow(1)+1, 'Delimiter', delimiter, 'TextType', 'string', 'ReturnOnError', false, 'EndOfLine', '\r\n');
for block=2:length(startRow)
    frewind(fileID);
    textscan(fileID, '%[^\n\r]', startRow(block)-1, 'WhiteSpace', '', 'ReturnOnError', false);
    dataArrayBlock = textscan(fileID, formatSpec, endRow(block)-startRow(block)+1, 'Delimiter', delimiter, 'TextType', 'string', 'ReturnOnError', false, 'EndOfLine', '\r\n');
    for col=1:length(dataArray)
        dataArray{col} = [dataArray{col};dataArrayBlock{col}];
    end
end

%% 关闭文本文件。
fclose(fileID);

%% 将包含数值文本的列内容转换为数值。
% 将非数值文本替换为 NaN。
raw = repmat({''},length(dataArray{1}),length(dataArray)-1);
for col=1:length(dataArray)-1
    raw(1:length(dataArray{col}),col) = mat2cell(dataArray{col}, ones(length(dataArray{col}), 1));
end
numericData = NaN(size(dataArray{1},1),size(dataArray,2));

for col=[1,2]
    % 将输入元胞数组中的文本转换为数值。已将非数值文本替换为 NaN。
    rawData = dataArray{col};
    for row=1:size(rawData, 1)
        % 创建正则表达式以检测并删除非数值前缀和后缀。
        regexstr = '(?<prefix>.*?)(?<numbers>([-]*(\d+[\,]*)+[\.]{0,1}\d*[eEdD]{0,1}[-+]*\d*[i]{0,1})|([-]*(\d+[\,]*)*[\.]{1,1}\d+[eEdD]{0,1}[-+]*\d*[i]{0,1}))(?<suffix>.*)';
        try
            result = regexp(rawData(row), regexstr, 'names');
            numbers = result.numbers;
            
            % 在非千位位置中检测到逗号。
            invalidThousandsSeparator = false;
            if numbers.contains(',')
                thousandsRegExp = '^[-/+]*\d+?(\,\d{3})*\.{0,1}\d*$';
                if isempty(regexp(numbers, thousandsRegExp, 'once'))
                    numbers = NaN;
                    invalidThousandsSeparator = true;
                end
            end
            % 将数值文本转换为数值。
            if ~invalidThousandsSeparator
                numbers = textscan(char(strrep(numbers, ',', '')), '%f');
                numericData(row, col) = numbers{1};
                raw{row, col} = numbers{1};
            end
        catch
            raw{row, col} = rawData{row};
        end
    end
end


%% 将非数值元胞替换为 NaN
R = cellfun(@(x) ~isnumeric(x) && ~islogical(x),raw); % 查找非数值元胞
raw(R) = {NaN}; % 替换非数值元胞

%% 创建输出变量
D = cell2mat(raw);

dlmwrite('bk.txt',D,'precision','%s','delimiter','\t'); % 数据保存

MIF文件读取结果
MIF文件读取结果

数据提取避免了循环,速度很快。自己读取CEIC目录56万行,耗时仅1秒。效率远远大于循环读取。MIF文件也仅仅需要5.7秒。

结语


最后。向提供工作便利的所有付出者致敬,向工作中的所有软件开发者致敬。

计算机的发展,虽然给我们带来了诸多便利。但是也让工作花样变得更多,更复杂,更累人,也很无聊。所以,写笔记找点乐子,没想到又花了好多时间。

最后的最后。感谢阅读,如有兴趣,欢迎留言交流哦。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 实例与代码
    • 交互操作函数
      • 数据导入
        • 导出代码
        • 举一反三
        • 结语
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档