2014-11-24 61 views
1

刚刚在R上做了一个项目,现在正在用matlab做一些工作。matlab中组织和搜索(日期,字符串,国家)

我需要做3个载体:

  1. DOD
  2. 国家
  3. 年龄

计数和存储与数据236点的列表的.txt的文本文件如下数据像这样:

Unknown woman 
Cause of death: found dead, with eyes removed. 
Location of death: Jardim dos Ipês Itaquaquecetuba, São Paulo, Brazil 
Date of death: August 9th, 2014 

Cris 
Cause of death: multiple gunshot wounds 
Location of death: Portal da Foz, Foz do Iguaçu, Brazil 
Date of death: September 13th, 2014 


Betty Skinner (52 years old) 
Cause of death: blunt force trauma to the head 
Location of death: Cleveland, Ohio, USA 
Date of death: December 4th, 2013 

Brittany Stergis (22 years old) 
Cause of death: gunshot wound to the head 
Location of death: Cleveland, Ohio, USA 
Date of death: December 5th, 2013 

我不知道如何查找字符串并组织它们,但会很感激任何想法如何开始。

回答

1

您可以使用textscan将文件读取到字符串的单元数组中,然后使用regexp解析字符串以获取所需的字段。

首先,我们读到的文本文件转换成字符串单元阵列:

fid = fopen('deaths.txt'); 
scanned_fields = textscan(fid, '%s', 'Delimiter','\n'); 
text_array = scanned_fields{1}; 
fclose(fid); 

虽然textscan能够一些基本分析的,这不是因为我们正在做的事情足够复杂。所以我们只是用它来读取每一行作为一个字符串:格式%s意味着我们期待一个字符串,并且设置Delimiter\n意味着字符串由换行符分隔。

接下来,我们可以释放正则表达式的真棒力量来分析你的死女人的字符串:

format = { 
    '(?<name>[ \w]*)' 
    ' \(' 
    '(?<age>[\d]*)' 
    ' years old\) - Cause of death: ' 
    '(?<cause>[ \w]*)' 
    ' - Location of death: ' 
    '(?<city>[ \w]*)' 
    ', ' 
    '(?<province>[ \w]*)' 
    ', ' 
    '(?<country>[ \w]*)' 
    ' - Date of death: ' 
    '(?<date>[ ,\w]*)' 
}; 
format = [format{:}]; 

这里我们只是定义格式字符串。我已经打破了这一点,使它更清楚发生了什么事情。让我们通过它去行由行:

  • (?<name>[ \w]*)括号中指明,这是文本块(又名“令牌”),我们希望捕捉到。 ?<name>表示我们将称这个令牌为“名称”。最后,[ \w]*指定要匹配的文本类型。方括号内的内容指定要查找的字符:空格()和/或字母数字字符(\w)。方括号外的*表示我们将接受任意数量的这些字符。
  • \(接下来我们正在寻找一个空格和一个左括号。圆括号前面的反斜线表示我们正在寻找一个字面括号,即不应将此圆括号解释为要捕获的另一个标记的开始。
  • (?<age>[\d]*)捕获的另一个标记。这个称为“年龄”,包含任何数量的\d(数字字符)。
  • years old \) - Cause of death:更多需要的文字。再一次,我们将匹配这个文本,但我们不会捕获它(因为它不包含在括号内)。
  • (?<city>[ \w]*)捕获的另一个标记。这个称为“城市”,包含任意数量的空格和/或字母数字字符。
  • ,逗号,空间
  • (?<province>[ \w]*), (?<country>[ \w]*) - Date of death:你的想法
  • (?<date>[ ,\w]*)我们最终的道理,所谓的“日期”,其中包含任意数量的空格,逗号和/或字母数字字符。

然后我们解析字符串成一个结构数组:

parsed_fields = regexp(text_array, format, 'names'); 
parsed_fields = [parsed_fields{:}]' 

这是输出应该是什么样子:

>> parsed_fields(1) 
ans = 
     name: 'Jacqueline Cowdrey' 
     age: '50' 
     cause: 'unknown' 
     city: 'Worthing' 
    province: 'West Sussex' 
    country: 'United Kingdom' 
     date: 'November 20th, 2013' 

所以,你可以得到你的国家的矢量漂亮straightforward- ly:

Country = {parsed_fields.country}'; 

年龄是一个简单的数字转换:

Age_str = {parsed_fields.age}; 
Age = cellfun(@str2double, Age_str)'; 

日期作为字符串是很容易的:

Date_str = {parsed_fields.date}'; 

但它是很好的把它作为MATLAB“序列日期数字”,它允许算术计算和重新格式化为不同类型的代表性格式。不幸的是,将“20”换成“20”而与转换功能不兼容,所以我们需要先将“st”,“nd”,“rd”从“1st”,“2nd” ,“第三”等:

Date_str = regexprep(Date_str, '(?<day>[\d]+)(st|nd|rd|th)', '$<day>'); 
Date_num = datenum(Date_str, 'mmmm dd, yyyy'); 

其他一些注意事项:

  • 如果文件非常大,您可能希望使用fgetl一次读它一行(随后还一次解析一行),而不是像上面那样将整个文件读入内存。

  • 在您的示例中,它看起来像条目由一个额外的换行符分隔。我不知道如果是这样的情况下,您的实际数据,或者这只是一个计算器的事情,但如果你需要删除这些换行符你可以这样做:

    is_empty_line = cellfun(@isempty, text_array); 
    text_array = text_array(~is_empty_line); 
    
  • 在你的榜样,有很多的错别字(多余的空间在这里和那里,有时候冒号或破折号是其他符号)。如果您的实际数据中存在这些拼写错误,则需要调整格式规格以解决此问题。例如,您可以使用\s*\W\s*来匹配(任意数量的空白字符,单个非字母数字字符以及任意数量的空格字符),而不是使用-来匹配(空格,短划线,空格)。

  • 如果像format = [format{:}];Country = {parsed_fields.country}';语法看起来很奇怪你,这等同于:

    format = [format{1} format{2} format{3} ... format{end}]; 
    Country = cell(length(parsed_fields),1); 
    for ii = 1:length(parsed_fields) 
        Country{ii} = parsed_fields(ii).country; 
    end 
    
  • MATLAB R2014b增加了一个新datetime类,所以有可能是一个更好的方式来处理,时下。

+0

我需要学会提出更准确的问题。我更新了名称列表以更好地显示文件的外观。由于我不了解如何格式化问题中的文本,因此无法准确显示它看起来的样子。 – Dgales4130 2014-11-25 19:31:41

+0

这里是实际数据[链接](https://docs.google.com/a/uic.edu/document/d/1AsCTRuCpJpQ2PRQp-SY7u5iXK9ayLOsuHzOcd5haWgw/edit?usp=sharing) – Dgales4130 2014-11-25 19:39:46

+0

我看到了;格式有些不同,并不是每个条目都有相关的年龄。我将添加一个新的答案,该答案适用于您发布的数据。 – KQS 2014-11-25 22:38:57

0

对不起,我以前的答案;我误解了数据的格式。

如前,让我们先读文本文件转换成字符串单元阵列:

fid = fopen('deaths.txt'); 
scanned_fields = textscan(fid, '%s', 'Delimiter','\n'); 
text_array = scanned_fields{1}; 
fclose(fid); 

虽然textscan能够一些基本分析的,这不是因为我们正在做的事情足够复杂。所以我们只是用它来读取每一行作为一个字符串:格式%s意味着我们期待一个字符串,并且设置Delimiter\n意味着字符串由换行符分隔。

在您发布的样本数据中,每个条目都是4行(名称,原因,位置,日期),后跟空行。只要我们可以依赖这种格式,这提供了一种简单的方法来分割数据(而不是我在我以前的答案中提出的regexp解析)。

name_str_array = text_array(1:5:end); 
cause_str_array = text_array(2:5:end); 
loc_str_array = text_array(3:5:end); 
date_str_array = text_arary(4:5:end); 

因此,例如,name_strs将是每一个5日线,从线#1。同样,cause_strs每隔5行,从#2行开始。请注意数据中没有任何额外或缺失的行。

接下来我们将解析其中的每一个来获取我们想要的信息。在我之前的回答中,我建议一次解析所有字符串,但我认为如果我们一次只读一个条目会更容易理解。例如,让我们考虑第一个条目。

name_str = name_str_array{1}; 
loc_str = loc_str_array{1}; 
date_str = date_str_array{1}; 

让我们先从最简单的一个:解析日期。

date_format = 'Date of death:\s*(?<date>.*)'; 
parsed_fields = regexp(date_str, date_format, 'names'); 
DOD = parsed_fields.date; 

我们正在寻找的格式字符串Date of death:,后跟任意数量的空白字符(\s*),然后是文本的块(又名“令牌”),我们希望捕捉到:(?<date>.*)

圆括号表示这是我们希望捕获的令牌,?<date>表示我们希望将此令牌称为“日期”,并且.*指定要查找哪些字符。 .是通用通配符,即它匹配所有可能的字符。 *表示我们对任何数量的重复都感兴趣。所以本质上,这个.*的意思是“匹配字符串中的所有剩余字符”。

使用names选项调用regexp会导致它返回一个带有指定标记的结构作为其字段。


接下来,让我们来做国家。这一个有点棘手,因为有可变数量的城市/地区说明符。但这个国家永远是最后一个国家,所以这是我们要抓的。

country_format = '(?<country>\w[ \w]*)$'; 
parsed_fields = regexp(loc_str, country_format, 'names'); 
Country = parsed_fields.country; 

此格式规格是令牌(?<country>\w[ \w]*)后跟字符串(由特殊字符$表示)的端部。在令牌规范中,我们匹配一个字母数字字符(\w),后跟任意数量的空格和/或字母数字字符([ \w]*)。指定这种领先\w的原因是,我们不匹配前一个逗号和国家名称开头之间的空格。


最后,我们来做一下这个年龄。这是一个棘手的问题,因为不是每一个条目都有一个年龄。至少这很容易,因为年龄(如果存在的话)是该行中唯一的数字数据。因此:

age_format = '(?<age>[\d]+)'; 
parsed_fields = regexp(name_str, age_format, 'names'); 
if isempty(parsed_fields) 
    Age = -1; 
else 
    Age = str2double(parsed_fields.age); 
end 

格式说明仅仅是令牌(?<age>[\d]+),它指定我们寻找数字字符(\d),和我们正在寻找一个或多个它们中的(+)。

解析后,我们检查是否有匹配。如果不是(parsed_fields为空),则我们将Age赋值为-1。否则,我们将解析后的年龄字段转换为数字。


所以把他们放在一起:

date_format = 'Date of death:\s*(?<date>.*)'; 
country_format = '(?<country>\w[ \w]*)[\W]?$'; 
age_format = '(?<age>[\d]+)'; 

nEntries = length(date_str_array); 
DOD = cell(nEntries, 1); 
Country = cell(nEntries, 1); 
Age = zeros(nEntries, 1); 

for ii = 1:nEntries 
    name_str = name_str_array{ii}; 
    loc_str = loc_str_array{ii}; 
    date_str = date_str_array{ii}; 

    parsed_fields = regexp(date_str, date_format, 'names'); 
    assert(~isempty(parsed_fields), 'Could not parse date from:\n%s', date_str); 
    DOD{ii} = parsed_fields.date; 

    parsed_fields = regexp(loc_str, country_format, 'names'); 
    assert(~isempty(parsed_fields), 'Could not parse country from:\n%s', loc_str); 
    Country{ii} = parsed_fields.country; 

    parsed_fields = regexp(name_str, age_format, 'names'); 
    if isempty(parsed_fields) 
     Age(ii) = -1; 
    else 
     Age(ii) = str2double(parsed_fields.age); 
    end 
end 

我加入了assert语句来帮助调试发生了什么事情,如果你在解析出现错误。

例如,您可能还会注意到我在国家/地区格式中添加了[\W]?。这是因为,在您的示例数据上运行它时,我遇到了一个国家,该国在该行末尾包含一段时间(即以“巴西”而不是“巴西”结尾)。因此,现在我们希望匹配一个非字母数字字符(\W)重复零次或一次(?),并且它在括号外,因此它不会被捕获为“国家”标记的一部分。

+0

我知道阵列结构正在扫描每条线,但之后会变得模糊。再一次,我对这一切都很陌生。我在哪里保存阵列?我运行你放的较长的帖子+数组'name_str_array = text_array(1:5:结束);' – Dgales4130 2014-11-26 01:27:37

+0

它正在读取,但我需要存储数组。我是一个noob。 – Dgales4130 2014-11-26 02:05:35

+0

如何储存和汇总所有国家? – Dgales4130 2014-11-26 03:07:39