【问题标题】:Efficient way to create term density matrix from pandas DataFrame从 pandas DataFrame 创建术语密度矩阵的有效方法
【发布时间】:2014-04-07 23:43:37
【问题描述】:

我正在尝试从 pandas 数据框中创建术语密度矩阵,以便对出现在数据框中的术语进行评分。我还希望能够保留我的数据的“空间”方面(有关我的意思的示例,请参阅帖子末尾的评论)。

我是 pandas 和 NLTK 的新手,所以我希望我的问题可以通过一些现有工具解决。

我有一个包含两列感兴趣的数据框:比如“标题”和“页面”

    import pandas as pd
    import re

    df = pd.DataFrame({'title':['Delicious boiled egg','Fried egg ','Split orange','Something else'], 'page':[1, 2, 3, 4]})
    df.head()

       page                 title
    0     1  Delicious boiled egg
    1     2            Fried egg 
    2     3          Split orange
    3     4        Something else

我的目标是清理文本,并将感兴趣的术语传递给 TDM 数据框。我使用两个函数来帮助我清理字符串

    import nltk.classify
    from nltk.tokenize import wordpunct_tokenize
    from nltk.corpus import stopwords
    import string   

    def remove_punct(strin):
        '''
        returns a string with the punctuation marks removed, and all lower case letters
        input: strin, an ascii string. convert using strin.encode('ascii','ignore') if it is unicode 
        '''
        return strin.translate(string.maketrans("",""), string.punctuation).lower()

    sw = stopwords.words('english')

    def tok_cln(strin):
        '''
        tokenizes string and removes stopwords
        '''
        return set(nltk.wordpunct_tokenize(strin)).difference(sw)

还有一个执行数据帧操作的函数

    def df2tdm(df,titleColumn,placementColumn,newPlacementColumn):
        '''
        takes in a DataFrame with at least two columns, and returns a dataframe with the term density matrix
        of the words appearing in the titleColumn
        Inputs: df, a DataFrame containing titleColumn, placementColumn among others
        Outputs: tdm_df, a DataFrame containing newPlacementColumn and columns with all the terms in df[titleColumn]
        '''
        tdm_df = pd.DataFrame(index=df.index, columns=[newPlacementColumn])
        tdm_df = tdm_df.fillna(0)
        for idx in df.index:
            for word in tok_cln( remove_punct(df[titleColumn][idx].encode('ascii','ignore')) ):
                if word not in tdm_df.columns:
                    newcol = pd.DataFrame(index = df.index, columns = [word])
                    tdm_df = tdm_df.join(newcol)
        tdm_df[newPlacementColumn][idx] = df[placementColumn][idx]
        tdm_df[word][idx] = 1
        return tdm_df.fillna(0,inplace = False)

    tdm_df = df2tdm(df,'title','page','pub_page')
    tdm_df.head()

返回

      pub_page boiled egg delicious fried orange split something else
    0        1      1   1         1     0      0     0         0    0
    1        2      0   1         0     1      0     0         0    0
    2        3      0   0         0     0      1     1         0    0
    3        4      0   0         0     0      0     0         1    1

但是在解析大型集合(数十万行、数千列的输出)时,速度非常慢。我的两个问题:

我可以加快这个实现吗?

我可以使用其他工具来完成这项工作吗?

我希望能够保留数据的“空间”方面,例如,如果“egg”经常出现在第 1-10 页,然后又经常出现在第 500-520 页,我想知道这一点。

【问题讨论】:

    标签: python r pandas nltk


    【解决方案1】:

    你可以使用scikit-learn的CountVectorizer

    In [14]: from sklearn.feature_extraction.text import CountVectorizer
    
    In [15]: countvec = CountVectorizer()
    
    In [16]: countvec.fit_transform(df.title)
    Out[16]: 
    <4x8 sparse matrix of type '<type 'numpy.int64'>'
        with 9 stored elements in Compressed Sparse Column format>
    

    它以稀疏表示返回术语文档矩阵,因为这样的矩阵通常很大,而且很稀疏。

    对于您的特定示例,我猜想将其转换回 DataFrame 仍然有效:

    In [17]: pd.DataFrame(countvec.fit_transform(df.title).toarray(), columns=countvec.get_feature_names())
    Out[17]: 
       boiled  delicious  egg  else  fried  orange  something  split
    0       1          1    1     0      0       0          0      0
    1       0          0    1     0      1       0          0      0
    2       0          0    0     0      0       1          0      1
    3       0          0    0     1      0       0          1      0
    
    [4 rows x 8 columns]
    

    【讨论】:

    • 太好了,sklearn 的实现比我的快 80 倍。谢谢!
    • 我在一个函数中实现了您的代码,但它给我的数据带来了非常大的内存开销。我为此创建了一个新线程,请参阅this link。任何进一步的帮助将不胜感激!谢谢!
    • 在最后一步中,.toarray() 对我不起作用,但 .todense() 对我起作用。
    【解决方案2】:

    herrfz 提供了一种处理此问题的方法,但我只想指出,使用 Python 集合创建术语密度数据结构会适得其反,因为集合是唯一对象的集合。您将无法捕获每个单词的计数,只能捕获给定行的单词。

    return set(nltk.wordpunct_tokenize(strin)).difference(sw)
    

    为了去除停用词,您可以执行类似

    的操作
    tokens_stripped = [token for token in tokens 
                       if token not in stopwords]
    

    标记化之后。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-12
      • 2015-12-22
      相关资源
      最近更新 更多