一、问题描述

对csv、xlsx等类型做词典法分析,经常用到apply方法,但是之前分享到案例数据量比较小, 词典都是几个词, 一般都能运行出结果。

liwc其实就是一种词典法

但这周末,我使用1.4G的mda数据集, 5w条记录。尝试计算某类词的出现次数, 该词典含几百个词。在我的96G内存的macbook中,运行了十几个小时都没结果。

于是同一个问题,本文分享了两种实现方法。一般情况下,使用「方法一」即可。当第一种方法运行不出结果,可以尝试「方法二」。



二、方法一

2.1 读取数据

导入含5000条记录的mda数据 test_mda.csv ,这里声明格式,防止年份和股票代码被识别为数字。

如果想要完整的mda数据,可以前往购买[]


import pandas as pd

#导入1000条测试数据
df = pd.read_csv('test_mda.csv', converters={'year': str, 'code': str})
df.head()


len(df)
5000

2.2 准备词典

为了节约时间,也构造了只有几个词语的词典。

自己随机手写的词典,如果需要应用的自己的研究中, 请将这几个概念词典扩充的完备一些,词汇量尽可能多一些。

#创新
inovation_words = ['创新', '科技', '研发', '高校', '技术', '科学', '理论', '专利', '攻克', '改良', '工艺', '前沿', '尖端']

#环保
green_words = ['绿色', '节能', '低碳', '环保', '环境友好', '无污染']

#短视主义
push_words = ['加快', '尽快', '抓紧', '月底', '年底', '争取', '马上', '立刻', '年内', '数月', '数年']

#模棱两可

prob_words = ['可能', '大概', '左右', '估计', '大约']

2.3 设计函数

该函数能实现对 inovation_words 和 green_words两类词的词频统计;

返回的结果包括总词频、green词频、 inovation词频。

import jieba
import pandas as pd

#创新
inovation_words = ['创新', '科技', '研发', '高校', '技术', '科学', '理论', '专利', '攻克', '改良', '工艺', '前沿', '尖端', '']
#环保
green_words = ['绿色', '节能', '低碳', '环保', '环境友好', '无污染']
#短视主义
push_words = ['加快', '尽快', '抓紧', '月底', '年底', '争取', '马上', '立刻', '年内', '数月', '数年']
#模棱两可
prob_words = ['可能', '大概', '左右', '估计', '大约']


def analysis_info(text):
    inovation_num, green_num, push_num, prob_num = 0, 0, 0, 0
    words = jieba.lcut(text)
    push_num 
    
    for w in inovation_words:
        inovation_num = inovation_num + words.count(w)

    for w in green_words:
        green_num = green_num + words.count(w)
        
    for w in push_words:
        push_num = push_num + words.count(w)
        
    for w in prob_words:
        prob_num = prob_num + words.count(w)
        
    res = {
        'words': len(words),
        'inovation': inovation_num,
        'green': green_num,
        'push': push_num,
        'prob': prob_num
    }
    
    return pd.Series(res)

analysis_info(text='2022年是公司规范运作,坚持科技创新,保持持续发展。')
words        15
inovation     2
green         0
push          0
prob          0
dtype: int64

2.4 批量计算

选中text列,对该列批量运行 analysis_info 。

import time

start = time.time()

info_df = df['text'].apply(analysis_info)
res_df = pd.concat([df, info_df], axis=1)

end = time.time()

print('耗时 {} s'.format(int(end-start)))

res_df.head()

耗时 112 s



三、方法二

将中文分词后, 使用bag-of-words构造词语词频矩阵

import pandas as pd 
from sklearn.feature_extraction.text import CountVectorizer
import time
import jieba

start = time.time()

df['new_text'] = df['text'].apply(lambda text: ' '.join(jieba.cut(text)))

vectorize = CountVectorizer()
dtm = vectorize.fit_transform(df.new_text)
bagofword_df = pd.DataFrame(dtm.toarray(), 
                            columns=vectorize.get_feature_names_out()) 


#创新
inovation_words = ['创新', '科技', '研发', '高校', '技术', '科学', '理论', '专利', '攻克', '改良', '工艺', '前沿', '尖端']
#环保
green_words = ['绿色', '节能', '低碳', '环保', '环境友好', '无污染']
#短视主义
push_words = ['加快', '尽快', '抓紧', '月底', '年底', '争取', '马上', '立刻', '年内', '数月', '数年']
#模棱两可
prob_words = ['可能', '大概', '左右', '估计', '大约']

INOVATION_WORDS = [w for w in inovation_words if w in bagofword_df.columns]
GREEN_WORDS = [w for w in green_words if w in bagofword_df.columns]
PUSH_WORDS = [w for w in push_words if w in bagofword_df.columns]
PROB_WORDS = [w for w in prob_words if w in bagofword_df.columns]

data = {'year': df['year'],
        'code': df['code'],
        'text': df['text'],
        
        'inovation': bagofword_df[INOVATION_WORDS].sum(axis=1),
        'green': bagofword_df[GREEN_WORDS].sum(axis=1),
        'push': bagofword_df[PUSH_WORDS].sum(axis=1),
        'prob': bagofword_df[PROB_WORDS].sum(axis=1),
        'words': bagofword_df.apply(sum, axis=1)
        }


res_df = pd.DataFrame(data)
end = time.time()

print('耗时 {} s'.format(int(end-start)))

res_df.head()

耗时 164 s



四、讨论

analysis_info(text) 函数内含有4个for循环,for循环是效率很低的操作。 随着所要计算的词典数n的增加, 方法一时间会随着n的增长而线性增长。

而方法二, 最费时间的瓶颈是将文本转化为数字(比较费时间),后续的计算均为向量化(矩阵化)的数值计算,随着词典数n的增加, 所消耗的时间会越来越短。

一般情况下,使用「方法一」即可。当第一种方法运行不出结果,可以尝试「方法二」。



广而告之