在Matlab中解析PC轴(.px)文件

在Matlab中解析PC轴(.px)文件,matlab,text-parsing,Matlab,Text Parsing,背景:PC Axis是一种用于传播统计信息的文件格式。许多国家统计组织使用该格式传播官方统计数据 PC Axis文件看起来有点像这样,尽管它们通常要长得多: CHARSET=”ANSI”; MATRIX="BE001"; SUBJECT-CODE="BE"; SUBJECT-AREA="Population"; TITLE="Population by region, time, marital status and sex."; Data= ".." ".." ".." ".." ".."

背景:PC Axis是一种用于传播统计信息的文件格式。许多国家统计组织使用该格式传播官方统计数据

PC Axis文件看起来有点像这样,尽管它们通常要长得多:

CHARSET=”ANSI”;
MATRIX="BE001";
SUBJECT-CODE="BE";
SUBJECT-AREA="Population";
TITLE="Population by region, time, marital status and sex.";
Data=
".." ".." ".." ".." ".." 
".." ".." ".." ".." ".." 
".." 24.80 34.20 52.00 23.00 
".." 32.10 40.30 50.70 1.00 
".." 31.60 35.00 49.10 2.30 
41.20 43.00 50.80 60.10 0.00 
50.90 52.00 53.90 65.90 0.00 
28.90 31.80 39.60 51.00 0.00;
有关PC Axis文件的更多详细信息可以在中找到,但基本要点是元数据位于文件的顶部,在“DATA=”之后是实际数据本身。还值得注意的是,数据的组织更像是一个数据表,而不是列

问题:我想用Matlab解析PC Axis文件,但我有点困惑,不知道该怎么做。有人知道如何在Matlab中解析这些文件吗?使用其他语言(如Perl)解析这种类型的文件,然后将数据导入Matlab会更容易吗?或者,Matlab是一个足够适合这项工作的工具吗?注意,计划是在文本处理阶段之后在Matlab中分析数据

我尝试过使用Matlab的文本处理工具,如fgetl、textscan、fscanf和其他一些工具,但这非常棘手。有人对如何着手做这件事有什么建议吗

本质上,我希望将每个关键字(字符集、矩阵等)及其对应的值(ANSI、BE001等)作为元数据存储在Matlab中——也许是作为一种结构。我也希望将数据存储在Matlab中,例如,作为矩阵

注意:我知道in R,它将.px文件作为data.frame对象读入工作区。还有一个叫做Perl的模块,它也很好,但我特别想知道如何使用Matlab解析.px文件

更新:我应该提到,除了元数据和数据之外,还有变量。这最好用一个例子来解释。下面的示例PC Axis文件与上面的相同,只是我添加了两个变量。它们被命名为值(“月”)和值(“区域”),位于元数据之后和数据之前

CHARSET=”ANSI”;
MATRIX="BE001";
SUBJECT-CODE="BE";
SUBJECT-AREA="Population";
TITLE="Population by region, time, marital status and sex.";
VALUES("Month")="1976M01","1976M02","1976M03","1976M04",
"1976M05","1976M06","1976M07","1976M08",
"1976M09","1976M10","1976M11","1976M12";
VALUES("region")="Sweden","Germany","France",
"Ireland","Finland";
Data=
".." ".." ".." ".." ".." 
".." ".." ".." ".." ".." 
".." 24.80 34.20 52.00 23.00 
".." 32.10 40.30 50.70 1.00 
".." 31.60 35.00 49.10 2.30 
41.20 43.00 50.80 60.10 0.00 
50.90 52.00 53.90 65.90 0.00 
28.90 31.80 39.60 51.00 0.00;
当以字符串形式(在单元格数组中)读取文本文件的每一行时,Textscan起到了处理作用。但是,两个变量(即值(“月”)和值(“区域”)的“=”符号后的元素跨越多行。在本例中使用textscan似乎意味着必须连接一些字符串,例如,为了收集月份列表(1976M01到1976M12)


问题:收集变量数据的最佳方式是什么?将文本文件作为单个字符串读取,然后使用strtok两次提取日期的子字符串?也许有更好的(更系统的)方法?

我个人对PC Axis文件并不熟悉,但以下是我的想法

首先解析头。如果标题大小固定,那么可以读入那么多行并解析出所需的值。regexp方法对此可能很有用

数据似乎是字符串和数字。我会将“.”值更改为NaN(当然,首先进行原始备份),然后使用textscan扫描矩阵。Textscan可能很棘手,因此请确保文件完全解析。如果textscan遇到与格式字符串不匹配的行,它将停止解析。您可以检查文件句柄的位置(使用ftell)以查看它是否与文件结尾匹配(您可以fseek到文件结尾以查找该值应该是什么)。textscan返回的单元格数组的长度应该都相同。如果没有,则长度将告诉您哪一行失败-您可以使用文本编辑器检查这一行,查看哪一行违反了格式

可以使用字符串参数指定和访问Matlab结构中的字段。例如:

foo.('a') = 1;
foo.a
ans = 
     1

因此,我建议的工作流程是解析标题行,将每个属性/值对指定为struct中的字段/值对。然后解析矩阵(经过一些简短的文本预处理以确保所有数据都是数字)。

通常
textscan
regexp
是解析字符串字段的方法(如图所示):

  • 使用
    textscan
    以字符串形式读取输入行:

    fid = fopen('input.px', 'r');
    C = textscan(fid, '%s', 'Delimiter', '\n');
    fclose(fid);
    
  • 使用
    regexp
    解析标题字段名称和值。选择正确的正则表达式应该可以做到这一点

    X = regexp(C{:}, '^\s*([^=\(\)]+)\s*=\s*"([^"]+)"\s*', 'tokens');
    X = [X{:}];                          %// Flatten the cell array
    X = reshape([X{:}], 2, []);          %// Reshape into name-value pairs
    
  • “值”字段可能跨越多行,因此需要先将它们连接起来:

    idx_data = find(~cellfun('isempty', regexp(C{:}, '^\s*Data')), 1);
    idx_values = find(~cellfun('isempty', regexp(C{:}, '^\s*VALUES')));
    Y = arrayfun(@(m, n){[C{:}{m:m + n - 1}]}, ...
       idx_values(idx_values < idx_data), diff([idx_values; idx_data]));
    
  • 确保字段名合法(我已决定将所有内容转换为小写,并用下划线替换撇号和任何空格),然后将它们插入结构:

    X = [X, Y];                             %// Store all fields in one array
    X(1, :) = lower(regexprep(X(1, :), '-+|\s+', '_')); 
    S = struct(X{:});
    
  • 以下是我从您的输入文件中获得的信息(仅标题字段):

    至于数据本身,需要单独处理:

  • 提取“数据”字段后的数据行,并用默认值替换所有
    。“
    值(例如,
    NaN
    ):

    显然,这假设在“数据”字段之后只有数字数据。但是,如果情况并非如此,则可以很容易地修改此项

  • 将数据转换为数字矩阵并将其添加到结构中:

    D = cellfun(@str2num, D, 'UniformOutput', false);
    S.data = vertcat(D{:})
    
  • 下面是输入文件的
    s.data

    S.data =
    
            NaN        NaN        NaN        NaN        NaN
            NaN        NaN        NaN        NaN        NaN
            NaN   24.80000   34.20000   52.00000   23.00000
            NaN   32.10000   40.30000   50.70000    1.00000
            NaN   31.60000   35.00000   49.10000    2.30000
       41.20000   43.00000   50.80000   60.10000    0.00000
       50.90000   52.00000   53.90000   65.90000    0.00000
    

    希望这有帮助

    我很确定Matlab足以满足您的需要。我会试一试,然后发布你的具体挑战。例如,尝试使用textscan解析矩阵(请参阅下面的答案)。如果你遇到麻烦,发布你的特定文本扫描问题。祝你好运回答得很好,伊坦T!非常感谢你的帮助。我已经能够修改您建议的代码来处理一些我在最初的问题中没有提到的其他事情。我想知道你是否可以就我对这个问题的更新提供第二轮帮助。如果你能看一下,那就太好了。非常感谢你的帮助
    D = strrep(C{:}(idx_data + 1:end), '".."', 'NaN');
    
    D = cellfun(@str2num, D, 'UniformOutput', false);
    S.data = vertcat(D{:})
    
    S.data =
    
            NaN        NaN        NaN        NaN        NaN
            NaN        NaN        NaN        NaN        NaN
            NaN   24.80000   34.20000   52.00000   23.00000
            NaN   32.10000   40.30000   50.70000    1.00000
            NaN   31.60000   35.00000   49.10000    2.30000
       41.20000   43.00000   50.80000   60.10000    0.00000
       50.90000   52.00000   53.90000   65.90000    0.00000