Wolfram mathematica 高效地处理(和生成)大型文本文件

Wolfram mathematica 高效地处理(和生成)大型文本文件,wolfram-mathematica,Wolfram Mathematica,作为我工作的一部分,我正在处理非常大的文本文件,并在一定程度上分析它们的单词和短语频率。我在计算时间、内存限制和提取相关信息方面遇到了困难 对于这个程序,我需要一个大的文本文件(比如50MB),它已经被清理,变成小写。但除此之外,它只是非结构化文本。我试图生成“bigram”、“trigram”、“quadrams”和“fivegrams”的列表,分别是重复出现的两个、三个、四个和五个单词短语的组合(即,“我是”是一个bigram,“我是自由的”是一个trigram,“我总是自由的”是一个qua

作为我工作的一部分,我正在处理非常大的文本文件,并在一定程度上分析它们的单词和短语频率。我在计算时间、内存限制和提取相关信息方面遇到了困难

对于这个程序,我需要一个大的文本文件(比如50MB),它已经被清理,变成小写。但除此之外,它只是非结构化文本。我试图生成“bigram”、“trigram”、“quadrams”和“fivegrams”的列表,分别是重复出现的两个、三个、四个和五个单词短语的组合(即,“我是”是一个bigram,“我是自由的”是一个trigram,“我总是自由的”是一个quadram)

我目前在做什么?

这是我当前的代码,其中
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
    快得多。这也会将其分解为单词

  • <> >您可以考虑使用GZIP压缩文件。<代码>导入< /代码> /<代码>导出< /C> >无缝地支持,但是<代码> Read List不。对于磁盘有限的操作,这将比读/写未压缩的数据快。

  • 您的
    排序可能很慢(我还没有用大文件测试您的操作,所以我不确定)

  • 在导出之前,您不能从
    计数
    中分离出来,但始终可以使用
    选择
    案例
    删除案例
    来修剪双字符列表

    最后,作为对您最后一个问题的回答:恐怕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>>}}}