Wolfram mathematica 高效地处理(和生成)大型文本文件
作为我工作的一部分,我正在处理非常大的文本文件,并在一定程度上分析它们的单词和短语频率。我在计算时间、内存限制和提取相关信息方面遇到了困难 对于这个程序,我需要一个大的文本文件(比如50MB),它已经被清理,变成小写。但除此之外,它只是非结构化文本。我试图生成“bigram”、“trigram”、“quadrams”和“fivegrams”的列表,分别是重复出现的两个、三个、四个和五个单词短语的组合(即,“我是”是一个bigram,“我是自由的”是一个trigram,“我总是自由的”是一个quadram) 我目前在做什么? 这是我当前的代码,其中Wolfram mathematica 高效地处理(和生成)大型文本文件,wolfram-mathematica,Wolfram Mathematica,作为我工作的一部分,我正在处理非常大的文本文件,并在一定程度上分析它们的单词和短语频率。我在计算时间、内存限制和提取相关信息方面遇到了困难 对于这个程序,我需要一个大的文本文件(比如50MB),它已经被清理,变成小写。但除此之外,它只是非结构化文本。我试图生成“bigram”、“trigram”、“quadrams”和“fivegrams”的列表,分别是重复出现的两个、三个、四个和五个单词短语的组合(即,“我是”是一个bigram,“我是自由的”是一个trigram,“我总是自由的”是一个qua
inputlower
是一个全小写字符串(带Mathematica的刮取web数据)
在某种程度上,它工作得很好:我确实得到了生成的信息,在较小的范围内,我发现这段代码的工作速度足够快,我可以得到一些近似于一个可行的操作[]
程序的东西。但是当我们处理大的输入时
当我使用大文件时,它有什么问题?
最重要的是,我的输出文件太大,无法使用。有没有办法在代码中指定断点:例如,我不希望任何“bigram”只出现一次?如果这证明仍然留下了太多的信息,有没有办法指定我不希望文件中出现任何“bigram”,除非它们出现超过10次?i、 e.如果“我的奶酪”出现20次,我想知道它,但如果“我的键盘”只出现一次,可能丢失它会使文件更易于管理
其次,这些过程需要很长时间:仅生成二元内存输出就需要两三个多小时。我是否以有效的方式处理这个问题
第三,如果我确实有一个包含所有信息的大型bigram文件(~650MB+),Mathematica有没有办法访问信息而不将其全部加载到内存中?例如,获取一个名为bigrams.txt的文件,了解它包含{“I”,“am”},55}
,而不会使系统陷入困境
编辑
[截至2011年12月7日,我已经删除了我创建的示例文件-再次感谢大家]您可能会看到”,这是Mathematica版本的正则表达式。可能类似于StringCases[data,RegularExpression[“\\w+?\\w+?\\w+?\\w+?”]
,它应该返回所有匹配的单词空格单词序列。我不能说这是否会比分区代码快
在那页的底部有一个“有效匹配的提示”
您可以在排序之前申请修剪列表
如果我用另一种语言做这件事,我会将n-gram存储在a中,文本作为键,实例计数作为值。这在逐行文件解析器中很好地工作。但是,使用a似乎并不简单
还有一个观察结果:您可以不运行4次过程,而是将所有5克数据生成到一个文件中,然后使用simple从该文件生成2克、3克和4克数据。当然,只有在合理的时间内运行5克提取器后,这才有用。以下是我的建议:
ReadList[file,Word]
。通常它比Import
快得多。这也会将其分解为单词排序可能很慢(我还没有用大文件测试您的操作,所以我不确定)
计数
中分离出来,但始终可以使用选择
、案例
或删除案例
来修剪双字符列表
最后,作为对您最后一个问题的回答:恐怕Mathematica是唯一有效/方便的工具,您可以将所有数据加载到内存中。该系统似乎只适用于内存中的数据。这是个人经验
编辑处理您的50 MB文本文件的速度很慢,但在我的(相当旧且速度慢的)机器上仍然可以忍受。只需确保您使用
排序
:
In[1]:= $HistoryLength = 0; (* save memory *)
In[2]:= Timing[
data = ReadList["~/Downloads/lowered-text-50.txt", Word,
WordSeparators -> {" ", "\t", ".", ","}];]
Out[2]= {6.10038, Null}
In[3]:= Timing[counts = Tally@Partition[data, 2, 1];]
Out[3]= {87.3695, Null}
In[4]:= Timing[counts = SortBy[counts, Last];]
Out[4]= {28.7538, Null}
In[5]:= Timing[counts = DeleteCases[counts, {_, 1}];]
Out[5]= {3.11619, Null}
我无法使用
ReadList
来正确处理UTF-8,因此您可能需要坚持使用那里的Import
。扩展我的评论,是ReadList
或Import
的有用替代方法。像ReadList
,您可以指定一个类型,如果指定字符串,它将读取整行内容。因此,您可以一次处理一行(或多行)整个文件。唯一的困难是您必须手动查看endofile
。例如
strm = OpenRead[file];
While[ (line = Read[ str, String ]) =!= EndOfFile,
(* Do something with the line *)
];
Close[ strm ];
要一次扩展到多行,请将上面的String
替换为只包含String
的要一次处理的行数的列表。最好使用多行来完成此操作。当然,也可以使用Word
来逐字处理文件。
逐行处理文件有一个缺点,如果您需要中止该过程,strm
将保持打开状态。因此,我建议将代码包装起来,或使用所述功能。这些幻灯片是当前处理导入和处理大数据集的最佳方法:
它涵盖了这里提到的一些主题,并给出了一些图表,这些图表将向您展示从导入切换到非导入的速度有多快。Introduction
我的建议与mos不同
strm = OpenRead[file];
While[ (line = Read[ str, String ]) =!= EndOfFile,
(* Do something with the line *)
];
Close[ strm ];
Clear[words];
words[text_String] := ToLowerCase[StringCases[text, WordCharacter ..]];
(* Rules to replace words with integer indices, and back *)
Clear[makeWordIndexRules];
makeWordIndexRules[sym_Symbol, words : {__String}] :=
With[{distinctWords = DeleteDuplicates[words]},
sym["Direct"] = Dispatch[Thread[distinctWords -> Range[Length[distinctWords]]]];
sym["Inverse"] = Dispatch[Thread[ Range[Length[distinctWords]] -> distinctWords]];
sym["keys"] = distinctWords;
];
(* Make a symbol with DownValues / OwnValues self - uncompressing *)
ClearAll[defineCompressed];
SetAttributes[defineCompressed, HoldFirst];
defineCompressed[sym_Symbol, valueType_: DownValues] :=
With[{newVals =
valueType[sym] /.
Verbatim[RuleDelayed][
hpt : Verbatim[HoldPattern][HoldPattern[pt_]], rhs_] :>
With[{eval = Compress@rhs}, hpt :> (pt = Uncompress@ eval)]
},
ClearAll[sym];
sym := (ClearAll[sym]; valueType[sym] = newVals; sym)
];
(* Get a list of indices corresponding to a full list of words in a text *)
Clear[getWordsIndices];
getWordsIndices[sym_, words : {__String}] :=
Developer`ToPackedArray[words /. sym["Direct"]];
(* Compute the combinations and their frequencies *)
Clear[getSortedNgramsAndFreqs];
getSortedNgramsAndFreqs[input_List, n_Integer] :=
Reverse[#[[Ordering[#[[All, 2]]]]]] &@ Tally[Partition[input, n, 1]];
(*
** Produce n-grams and store them in a hash-table. We split combinations from
** their frequencies, and assume indices for input, to utilize packed arrays
*)
Clear[produceIndexedNgrams];
produceIndexedNgrams[sym_Symbol, input_List, range : {__Integer}] :=
Do[
With[{ngramsAndFreqs = getSortedNgramsAndFreqs[input, i]},
sym["NGrams", i] = Developer`ToPackedArray[ngramsAndFreqs[[All, 1]]];
sym["Frequencies", i] = Developer`ToPackedArray[ngramsAndFreqs[[All, 2]]]
],
{i, range}];
(* Higher - level function to preprocess the text and populate the hash - tables *)
ClearAll[preprocess];
SetAttributes[preprocess, HoldRest];
preprocess[text_String, inputWordList_Symbol, wordIndexRuleSym_Symbol,
ngramsSym_Symbol, nrange_] /; MatchQ[nrange, {__Integer}] :=
Module[{},
Clear[inputWordList, wordIndexRuleSym, ngramsSym];
inputWordList = words@text;
makeWordIndexRules[wordIndexRuleSym, inputWordList];
produceIndexedNgrams[ngramsSym,
getWordsIndices[wordIndexRuleSym, inputWordList], nrange]
];
(* Higher - level function to make the definitions auto-uncompressing and save them*)
ClearAll[saveCompressed];
SetAttributes[saveCompressed, HoldRest];
saveCompressed[filename_String, inputWordList_Symbol, wordIndexRuleSym_Symbol,
ngramsSym_Symbol] :=
Module[{},
defineCompressed /@ {wordIndexRuleSym, ngramsSym};
defineCompressed[inputWordList, OwnValues];
DumpSave[filename, {inputWordList, wordIndexRuleSym, ngramsSym}];
];
test = Import["C:\\Temp\\lowered-text-50.txt", "Text"];
In[64]:= preprocess[test,inputlower,wordIndexRules,ngrams,{2,3}];//Timing
Out[64]= {55.895,Null}
In[65]:= ByteCount[inputlower]
Out[65]= 459617456
In[69]:= inputlower[[1000;;1010]]
Out[69]= {le,fort,edmonton,le,principal,entrepôt,de,la,compagnie,de,la}
In[67]:= toNumbers = inputlower[[1000;;1010]]/.wordIndexRules["Direct"]
Out[67]= {58,220,28,58,392,393,25,1,216,25,1}
In[68]:= toWords =toNumbers/. wordIndexRules["Inverse"]
Out[68]= {le,fort,edmonton,le,principal,entrepôt,de,la,compagnie,de,la}
In[70]:= {ngrams["NGrams",2],ngrams["Frequencies",2]}//Short
Out[70]//Short= {{{793,791},{25,1},{4704,791},<<2079937>>,{79,80},{77,78},{33,34}},{<<1>>}}
In[71]:= saveCompressed["C:\\Temp\\largeTextInfo.mx", inputlower,
wordIndexRules, ngrams] // Timing
Out[71]= {30.405, Null}
In[1]:= Get["C:\\Temp\\largeTextInfo.mx"] // Timing
Out[1]= {0.016, Null}
In[2]:= inputlower//Short//Timing
Out[2]= {6.52,{la,présente,collection,numérisée,<<8000557>>,quicktime,3,0}}
In[3]:= Timing[Short[ngrams2 = {ngrams["NGrams",2],ngrams["Frequencies",2]}]]
Out[3]= {0.639,{{{793,791},{25,1},{4704,791},<<2079937>>,{79,80},{77,78},{33,34}},{<<1>>}}}
In[4]:= Timing[Short[ngrams3 = {ngrams["NGrams",3],ngrams["Frequencies",3]}]]
Out[4]= {1.357,{{{11333,793,11334},{793,11334,11356},<<4642628>>,{18,21,22},{20,18,21}},{<<1>>}}}
In[5]:= Developer`PackedArrayQ/@ngrams3
Out[5]= {True,True}
In[6]:= Timing[Short[wordIndexRules["Inverse"]]]
Out[6]= {0.905,Dispatch[{1->la,2->présente,<<160350>>,160353->7631,160354->jomac},-<<14>>-]}
In[8]:= Position[ngrams["Frequencies",3],1,{1}]//Short//Timing
Out[8]= {1.404,{{870044},{870045},{870046},<<3772583>>,{4642630},{4642631},{4642632}}}
extractPositionFromSparseArray[HoldPattern[SparseArray[u___]]] := {u}[[4, 2, 2]];
positionExtr[x_List, n_] :=
extractPositionFromSparseArray[SparseArray[Unitize[x - n], Automatic, 1]]
In[9]:= positionExtr[ngrams["Frequencies",3],1]//Short//Timing
Out[9]= {0.156,{{870044},{870045},{870046},<<3772583>>,{4642630},{4642631},{4642632}}}
Clear[getNGramsWithFrequency];
getNGramsWithFrequency[ngramSym_Symbol, n_Integer, freq_Integer] :=
Extract[ngramSym["NGrams", n], positionExtr[ngramSym["Frequencies", n], freq]];
Clear[deleteNGramsWithFrequency];
deleteNGramsWithFrequency[{ngrams_List, freqs_List}, freq_Integer] :=
Delete[#, positionExtr[freqs, freq]] & /@ {ngrams, freqs};
deleteNGramsWithFrequency[ngramSym_Symbol, n_Integer, freq_Integer] :=
deleteNGramsWithFrequency[{ngramSym["NGrams", n], ngramSym["Frequencies", n]}, freq];
In[15]:= deleteNGramsWithFrequency[ngrams,2,1]//Short//Timing
Out[15]= {0.218,{{{793,791},{25,1},{4704,791},<<696333>>,{29,66},{36,37},{18,21}},{<<1>>}}}
In[17]:= (twogramsLarger100 =
Fold[deleteNGramsWithFrequency,deleteNGramsWithFrequency[ngrams,2,1],Range[2,100]])
//Short//Timing
Out[17]= {0.344,{{{793,791},{25,1},{4704,791},{25,10},<<6909>>,
{31,623},{402,13},{234,25}},{<<1>>}}}
In[18]:= twogramsLarger100/.wordIndexRules["Inverse"]//Short//Timing
Out[18]= {0.063,{{{of,the},{de,la},<<6912>>,{société,du},{processus,de}},{<<1>>}}}