中文新闻分类 数据集_【零基础入门NLP - 新闻文本分类】2.数据分析

在上一章节,我们给大家简单介绍了赛题的内容和几种解决方案。从本章开始我们将会逐渐带着大家使用思路1到思路4来完成本次赛题。在讲解工具使用的同时,我们还会讲解一些算法的原理和相关知识点,并会给出一定的参考文献供大家深入学习。

数据读取与数据分析

本章主要内容为数据读取和数据分析,具体使用Pandas库完成数据读取操作,并对赛题数据进行分析构成。

学习目标

  • 学习使用Pandas读取赛题数据
  • 分析赛题数据的分布规律

数据读取

赛题数据虽然是文本数据,每个新闻是不定长的,但任然使用csv格式进行存储。因此可以直接用Pandas完成数据读取的操作。

import pandas as pd
train_df = pd.read_csv('../input/train_set.csv', sep='t', nrows=100)

这里的read_csv由三部分构成:

  • 读取的文件路径,这里需要根据改成你本地的路径,可以使用相对路径或绝对路径;
  • 分隔符sep,为每列分割的字符,设置为t即可;
  • 读取行数nrows,为此次读取文件的函数,是数值类型(由于数据集比较大,建议先设置为100);

d0526f6ebd410f1f0d79ff0cb87f48ff.png

上图是读取好的数据,是表格的形式。第一列为新闻的类别,第二列为新闻的字符。

数据分析

在读取完成数据集后,我们还可以对数据集进行数据分析的操作。虽然对于非结构数据并不需要做很多的数据分析,但通过数据分析还是可以找出一些规律的。

train_df = pd.read_csv('./input/train_set.csv', sep='t')

此步骤我们读取了所有的训练集数据,在此我们通过数据分析希望得出以下结论:

  • 赛题数据中,新闻文本的长度是多少?
  • 赛题数据的类别分布是怎么样的,哪些类别比较多?
  • 赛题数据中,字符分布是怎么样的?

句子长度分析

在赛题数据中每行句子的字符使用空格进行隔开,所以可以直接统计单词的个数来得到每个句子的长度。统计训练集如下:

%pylab inline
train_df['text_len'] = train_df['text'].apply(lambda x: len(x.split(' ')))
print(train_df['text_len'].describe())

输出结果为:

Populating the interactive namespace from numpy and matplotlib
count    200000.000000
mean        907.207110
std         996.029036
min           2.000000
25%         374.000000
50%         676.000000
75%        1131.000000
max       57921.000000
Name: text_len, dtype: float64

对新闻句子的统计可以得出,本次赛题给定的文本比较长,每个句子平均由907个字符构成,最短的句子长度为2,最长的句子长度为57921。

测试集a的分布和训练集类似:

count    50000.000000
mean       909.844960
std       1032.313375
min         14.000000
25%        370.000000
50%        676.000000
75%       1133.000000
max      41861.000000
Name: text_len, dtype: float64

下图将句子长度绘制了直方图,可见大部分句子的长度都几种在2000以内。

import matplotlib.pyplot as plt
_ = plt.hist(train_df['text_len'], bins=200)
plt.xlabel('Text char count')
plt.title("Histogram of char count")

070825dffd56b253ceab6bcbaa8d4759.png

参数bins是指箱子的个数,即条状图的个数,这里是200,表示将范围内的文本长度均分成200个bin。

新闻类别分布

接下来可以对数据集的类别进行分布统计,具体统计每类新闻的样本个数。

train_df['label'].value_counts().plot(kind='bar')
plt.title('News class count')
plt.xlabel("category")

71756a2aeafcda0edb2b38349fd8fe8a.png

在数据集中标签的对应的关系如下:{'科技': 0, '股票': 1, '体育': 2, '娱乐': 3, '时政': 4, '社会': 5, '教育': 6, '财经': 7, '家居': 8, '游戏': 9, '房产': 10, '时尚': 11, '彩票': 12, '星座': 13}

从统计结果可以看出,赛题的数据集类别分布存在较为不均匀的情况。在训练集中科技类新闻最多,其次是股票类新闻,最少的新闻是星座新闻。

字符分布统计

接下来可以统计每个字符出现的次数,首先可以将训练集中所有的句子进行拼接进而划分为字符,并统计每个字符的个数。

from collections import Counter
# 所有的字符都是以空格分隔
all_lines = ' '.join(list(train_df['text']))
# 统计每个字符的个数,以字典{字符:出现次数}的形式
word_count = Counter(all_lines.split(" "))
# 按照字符的出现次数降序排序
word_count = sorted(word_count.items(), key=lambda d:d[1], reverse = True)

print(len(word_count))
# 6869

print(word_count[0])
# ('3750', 7482224)

print(word_count[-1])
# ('3133', 1)

print(word_count[:10])
# [('3750', 7482224), ('648', 4924890), ('900', 3262544), ('3370', 2020958), ('6122', 1602363), ('4464', 1544962), ('7399', 1455864), ('4939', 1387951), ('3659', 1251253), ('4811', 1159401)]

从统计结果中可以看出,在训练集中总共包括6869个字,其中编号3750的字出现的次数最多,编号3133的字出现的次数最少。

如果上述代码报错,出现"MemoryError",说明内存不够用了,可以选择换电脑、加内存条,或者采用下列代码:

%%time
vocab = dict()
for text in train_df['text']:
    for word in text.split():
        if vocab.get(word):
            vocab[word] += 1
        else:
            vocab[word] = 1
vocab = sorted(vocab.items(), key=lambda d:d[1], reverse = True)
print(len(vocab))
print(vocab[0])
print(vocab[-1])
print(vocab[:10])

可以看看字符的范围:

chars = sorted(vocab, key=lambda x: int(x[0]))
print(len(chars), chars[0], chars[-1])
# 6869 ('0', 24) ('7549', 12)

从0到7549一共6869个字符,说明中间有缺失。

这里还可以根据字在每个句子的出现情况,反推出标点符号。下面代码统计了不同字符在句子中出现的次数,其中字符3750,字符900和字符648在20w新闻的覆盖率接近99%,很有可能是标点符号。

# 句子中出现的字符集合(无重复字符)
train_df['text_unique'] = train_df['text'].apply(lambda x: ' '.join(list(set(x.split(' ')))))
# 所有数据的字符集合拼接
all_lines = ' '.join(list(train_df['text_unique']))
word_count = Counter(all_lines.split(" "))
word_count = sorted(word_count.items(), key=lambda d:int(d[1]), reverse = True)

print(word_count[0])
# ('3750', 197997)

print(word_count[1])
# ('900', 197653)

print(word_count[2])
# ('648', 191975)

print(word_count[:5])
# [('3750', 197997), ('900', 197653), ('648', 191975), ('2465', 177310), ('6122', 176543)]

看看测试集的:

all_lines = ' '.join(list(test_df['text']))
word_count_test = Counter(all_lines.split(" "))
word_count_test = sorted(word_count_test.items(), key=lambda d:d[1], reverse = True)

print(len(word_count_test))
# 6203

print(word_count_test[0])
# ('3750', 1879488)

print(word_count_test[-1])
# ('1224', 1)

test_df['text_unique'] = test_df['text'].apply(lambda x: ' '.join(list(set(x.split(' ')))))
all_lines = ' '.join(list(test_df['text_unique']))
word_count_test = Counter(all_lines.split(" "))
word_count_test = sorted(word_count_test.items(), key=lambda d:int(d[1]), reverse = True)

print(word_count_test[0])
# ('3750', 49455)

print(word_count_test[1])
# ('900', 49366)

print(word_count_test[2])
# ('648', 47966)

和训练集的情况类似。

训练集和测试集合在一起

vocab_all = dict()
for text in train_df['text']:
    for word in text.split():
        if vocab_all.get(word):
            vocab_all[word] += 1
        else:
            vocab_all[word] = 1
for text in test_df['text']:
    for word in text.split():
        if vocab_all.get(word):
            vocab_all[word] += 1
        else:
            vocab_all[word] = 1
chars_all = sorted(vocab_all.items(), key=lambda x: int(x[0]))
print(len(chars_all), chars_all[0], chars_all[-1])
word_count_all = sorted(vocab_all.items(), key=lambda d:d[1], reverse = True)
print(len(word_count_all), word_count_all[0], word_count_all[-1], word_count_all[:5])

输出:

6977 ('0', 31) ('7549', 14)
6977 ('3750', 9361712) ('1085', 1) [('3750', 9361712), ('648', 6157412), ('900', 4081309), ('3370', 2532394), ('6122', 2004576)]

假设字符3750,字符900和字符648是句子的标点符号,请分析赛题每篇新闻平均由多少个句子构成?

import re
# 训练集
train_df['sent_len'] = train_df['text'].apply(lambda x: len([x for x in re.split('3750|900|648', x) if x.strip()!='']))
print(train_df['sent_len'].describe())

# 测试集
test_df['sent_len'] = test_df['text'].apply(lambda x: len([x for x in re.split('3750|900|648', x) if x.strip()!='']))
print(test_df['sent_len'].describe())

输出结果:

# 训练集
count    200000.000000
mean         79.466030
std          85.383165
min           1.000000
25%          28.000000
50%          56.000000
75%         102.000000
max        3393.000000
Name: sent_len, dtype: float64

# 测试集
count    50000.000000
mean        79.747140
std         86.844506
min          1.000000
25%         28.000000
50%         56.000000
75%        102.000000
max       2751.000000
Name: sent_len, dtype: float64

所以每篇新闻平均由80个句子构成,最少的是1个句子,最多的是3000个左右。

统计每类新闻中出现次数最多的字符

for i in range(14):
    all_lines = ' '.join(list(train_df[train_df['label']==i]['text']))
    word_count_label = Counter([x for x in all_lines.split(" ") if x not in ['3750', '648', '900']])
    print(i, word_count_label.most_common(1))

这里去掉了可能是标点符号的字符的影响,输出结果

0 [('3370', 503768)]
1 [('3370', 626708)]
2 [('7399', 351894)]
3 [('6122', 187933)]
4 [('4411', 120442)]
5 [('6122', 159125)]
6 [('6248', 193757)]
7 [('3370', 159156)]
8 [('6122', 57345)]
9 [('7328', 46477)]
10 [('3370', 67780)]
11 [('4939', 18591)]
12 [('4464', 51426)]
13 [('4939', 9651)]

数据分析的结论

在数据集中标签的对应的关系如下:{'科技': 0, '股票': 1, '体育': 2, '娱乐': 3, '时政': 4, '社会': 5, '教育': 6, '财经': 7, '家居': 8, '游戏': 9, '房产': 10, '时尚': 11, '彩票': 12, '星座': 13}

1.文本长度:

  • 训练集和测试集a的文本长度分布基本一致
  • 训练集:文本长度的均值约为907字符,中值为676字符,最短文本为2字符,最长文本为57921字符
  • 测试集a:文本长度的均值约为910字符,中值为676字符,最短文本为14字符,最长文本为41861字符

2.新闻类别:

  • 科技类的样本数量最多:38918
  • 星座类的样本数量最少:908

3.字符分布:

  • 训练集总共6869个字符,从0到7549,中间不连续,有缺失
  • 训练集+测试集总共6977个字符,从0到7549
  • 出现频率最高的字符Top10:3750,648,900,3370,6122,4464,7399,4939,3659,4811
  • 字符3750,字符900和字符648在20w新闻的覆盖率接近99%,很有可能是标点符号。
  • 如果字符3750,900和648是标点符号,那么每篇新闻平均由80个句子构成,中值是56,最少的是1个句子,最多的是3000个左右。

通过上述分析我们可以得出以下结论:

  1. 赛题中每个新闻包含的字符个数平均为1000个,还有一些新闻字符较长;
  2. 赛题中新闻类别分布不均匀,科技类新闻样本量接近4w,星座类新闻样本量不到1k;
  3. 赛题总共包括7000-8000个字符;

通过数据分析,我们还可以得出以下结论:

  1. 每个新闻平均字符个数较多,可能需要截断;
  2. 由于类别不均衡,会严重影响模型的精度;

本章小结

本章对赛题数据进行读取,并新闻句子长度、类别和字符进行了可视化分析。

参考资料

https://github.com/datawhalechina/team-learning-nlp/tree/master/NewsTextClassification

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值