2014-11-24 56 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