本文将对新闻文本分类大赛数据进行读取和初步分析
数据读取
分别读取训练集与测试集:
data_train = pd.read_csv("train_set.csv",sep='\t')
data_test = pd.read_csv("test_a.csv")
这里pandas.read_csv()有很多参数,以下列举部分常用的并作简要解释:
-
sep: str, default ‘,’
指定分隔符。如果不指定参数,则会尝试使用逗号分隔。分隔符长于一个字符并且不是‘\s+’,将使用python的语法分析器。并且忽略数据中的逗号。正则表达式例子:’\r\t’,’\s+’->匹配任何空白字符,包括空格、制表符、换页符等等
-
delimiter : str, default None
备用分隔符(如果指定该参数,则sep参数失效) -
names: 设定列标题(前面分隔后会分成几列)
-
skiprows: list-like or interger, default None
需要忽略的行数,就是从第多少行开始读起
-
skip_blank_lines: boolean, default True
跳过空白行, 如果false,记为NaN
-
parse_dates : boolean or list of ints or names or list of lists or dict, default False
boolean. True -> 解析索引
list of ints or names. e.g. If [1, 2, 3] -> 解析1,2,3列的值作为独立的日期列;
list of lists. e.g. If [[1, 3]] -> 合并1,3列作为一个日期列使用
dict, e.g. {‘foo’ : [1, 3]} -> 将1,3列合并,并给合并后的列起名为”foo” -
encoding : str, default None
指定字符集类型,通常指定为’utf-8’. 在做Textmining的时候,有时候需要编码方式设为包容性更大的,比如’ISO8859-1’ -
nrows : int, default None
需要读取的行数(从文件头开始算起)。有时候文件过大,可以只读前多少行做测试,节省时间。
之后可以打印少许数据查看数据集内容的结构。
print(data_train.head())
print(data_test.head())
label | text | |
---|---|---|
0 | 2 | 2967 6758 339 2021 1854 3731 4109 3792 4149 15… |
1 | 11 | 4464 486 6352 5619 2465 4802 1452 3137 5778 54… |
2 | 3 | 7346 4068 5074 3747 5681 6093 1777 2226 7354 6… |
3 | 2 | 7159 948 4866 2109 5520 2490 211 3956 5520 549… |
4 | 3 | 3646 3055 3055 2490 4659 6065 3370 5814 2465 5… |
text | |
---|---|
0 | 5399 3117 1070 4321 4568 2621 5466 3772 4516 2… |
1 | 2491 4109 1757 7539 648 3695 3038 4490 23 7019… |
2 | 2673 5076 6835 2835 5948 5677 3247 4124 2465 5… |
3 | 4562 4893 2210 4761 3659 1324 2595 5949 4583 2… |
4 | 4269 7134 2614 1724 4464 1324 3370 3370 2106 2… |
可以发现训练集由文本类别与文本内容两列组成,而测试集只有一列文本,且所有的这些文本都被进行了匿名处理。
数据分析
句子长度分析
data_train['len'] = data_train['text'].apply(lambda x:len(x.split(' ')))
data_train['len'].describe()
这里使用str.split()来将文本分皆为元素为单个字符的列表,在求取列表的长度,即句子长度。之后用Series.describe()来获取这些长度数值的统计数据。
len | |
---|---|
count | 200000.000000 |
mean | 907.207110 |
std | 996.029036 |
min | 2.000000 |
25% | 374.000000 |
50% | 676.000000 |
75% | 1131.000000 |
max | 57921.000000 |
由上面的表格可以看出,这200000个文本平均句长为907.2,最大句长为57921,最短为2,也就是最短的句子仅仅只有两个字符。再者通过一分位(25%)和三分位(75%)我们可以得出句长大部分集中在300-1200范围之间。这里我们可以用直方图来更直观的表现。
import seaborn as sns
%pylab inline
#import matplotlib.pyplot as plt
sns.distplot(data_train['len'],bins = 200)
plt.xlabel('Text char count')
plt.title("Histogram of char count")
上面代码里的pylab是ipython里的magic function。他其实相当于以下代码:
import numpy
import matplotlib
from matplotlib import pylab, mlab, pyplot
np = numpy
plt = pyplotfrom IPython.display import display
from IPython.core.pylabtools import figsize, getfigsfrom pylab import *
from numpy import *
详细见:https://ipython.org/ipython-doc/2/api/generated/IPython.core.magics.pylab.html
新闻类别分布
用Series.value_counts()来获得各个类别出现的次数,再用seaborn.barplot()可视化出来。
class_count = pd.DataFrame(data_train['label'].value_counts()).reset_index()
sns.barplot(x='index',y = 'label',data = class_count)
plt.title('News class count')
plt.ylabel("count")
plt.xlabel("category")
在数据集中标签的对应的关系如下:{‘科技’: 0, ‘股票’: 1, ‘体育’: 2, ‘娱乐’: 3, ‘时政’: 4, ‘社会’: 5, ‘教育’: 6, ‘财经’: 7, ‘家居’: 8, ‘游戏’: 9, ‘房产’: 10, ‘时尚’: 11, ‘彩票’: 12, ‘星座’: 13}
可以发现科技类别的新闻文本是最多的,其次是股票,星座类别的文本最少。并且各类别的分布明显的不均匀,但也基本符合实际情况
字符分布统计
from collections import Counter
#由于数据集过大,这里做了一下采样
data_train_sample = data_train.sample(n=10000,random_state=405633)
all_lines = ' '.join(list(data_train_sample['text']))
word_count = Counter(all_lines.split(" "))
word_count = sorted(word_count.items(), key=lambda d:d[1], reverse = True)
print(len(word_count))
print(word_count[0])
print(word_count[-1])
上述代码先使用str.join(list)合并所有文本,之后转化为字符列表后再用collections.Counter函数对字符出现次数进行统计,然后将其以字典形式输出,最后根据字符频率进行排序,输出元素为元组的列表。
5327
[('3750', 372984), ('648', 244868), ('900', 162333), ('3370', 105991), ('4464', 82099)]
('6315', 1)
从输出结果看,总共有5327个字符,然后‘3750’出现的最多,37万多,这个量级非常的大,平均下来每篇文档出现37.29次,这里很有可能是标点。
#利用set的无重复性滤除重复字符
data_train_sample['text_unique'] = data_train_sample['text'].apply(lambda x: ' '.join(list(set(x.split(' ')))))
all_lines = ' '.join(list(data_train_sample['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[:5])
[('3750', 9890), ('900', 9883), ('648', 9594), ('2465', 8842), ('6122', 8819)]
可以发现’3750’和’900’这两个字符的覆盖率接近99%,所以这很大可能性是标点符号,当然也不排除某些常用的暂停词,比如“是”,“的”等。
句子数目预测
假设字符3750,字符900和字符648是句子的标点符号,那我们可以对句子数目进行统计
python里字符串的split只限单个分隔符对句子进行分隔,而re模块里的可以实现多个分隔符。
import re
data_train_sample['sent_len'] = data_train_sample['text'].apply(lambda x: len(re.split('3750|900|648',x)))
data_train_sample['sent_len'] .describe()
sent_len | |
---|---|
count | 10000.000000 |
mean | 80.465500 |
std | 87.870409 |
min | 1.000000 |
25% | 28.000000 |
50% | 57.000000 |
75% | 102.000000 |
max | 2193.000000 |
从上表得出这10000个样本文本的句子平均长度为80,最短为1,最长为2193。(前提是以3750,900和648为标点)
类别内字符统计
for i in range(14):data_class = data_train_sample[data_train_sample['label']==i]all_lines = ' '.join(list(data_class['text']))word_count = Counter(all_lines.split(" "))word_count = sorted(word_count.items(), key=lambda d:d[1], reverse = True)print("类别",i,":",word_count[:5])
类别 0 : [('3750', 62357), ('648', 46589), ('900', 28191), ('3370', 25582), ('4464', 15594)]
类别 1 : [('3750', 60881), ('648', 37043), ('3370', 34105), ('900', 27931), ('4464', 26135)]
类别 2 : [('3750', 72344), ('648', 48674), ('900', 30534), ('7399', 17182), ('6122', 16876)]
类别 3 : [('3750', 38544), ('648', 25423), ('900', 14495), ('6122', 9518), ('4939', 8796)]
类别 4 : [('3750', 18241), ('648', 11823), ('900', 9721), ('4411', 5942), ('7399', 4439)]
类别 5 : [('3750', 36044), ('648', 16526), ('900', 15214), ('6122', 8016), ('5598', 6901)]
类别 6 : [('3750', 24660), ('648', 17095), ('900', 11274), ('6248', 9687), ('2555', 9363)]
类别 7 : [('3750', 22275), ('648', 13586), ('900', 9600), ('3370', 8528), ('5296', 7448)]
类别 8 : [('3750', 11716), ('648', 9734), ('900', 4688), ('4939', 3072), ('6122', 2894)]
类别 9 : [('3750', 8187), ('648', 7348), ('900', 3208), ('7328', 2168), ('6122', 2043)]
类别 10 : [('3750', 8171), ('648', 4952), ('3370', 3481), ('900', 3443), ('4464', 2113)]
类别 11 : [('3750', 4263), ('648', 3426), ('900', 1804), ('5560', 1050), ('4939', 1035)]
类别 12 : [('3750', 4334), ('4464', 2787), ('3370', 2641), ('3659', 1907), ('900', 1869)]
类别 13 : [('3750', 967), ('648', 890), ('900', 361), ('4939', 293), ('6122', 277)]
可以看出每个类的出现最多的几个字符都是相似的,所以基本上就是标点或者暂停词了。
结论
通过上述分析我们可以得出以下结论:
- 赛题中每个新闻包含的字符个数平均为1000个,还有一些新闻字符较长;
- 赛题中新闻类别分布不均匀,科技类新闻样本量接近4w,星座类新闻样本量不到1k;
- 赛题总共包括7000-8000个字符;
通过数据分析,我们还可以得出以下结论:
- 每个新闻平均字符个数较多,可能需要截断;
- 由于类别不均衡,会严重影响模型的精度;
参考文献:
- https://ipython.org/ipython-doc/2/api/generated/IPython.core.magics.pylab.html