本文主要资源引用:https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.12586969.1002.9.6406111aIKCSLV&postId=118253
本章主要内容为数据读取和数据分析,具体使用Pandas
库完成数据读取操作,并对赛题数据进行分析构成。
分别读取训练集与测试集:
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
需要读取的行数(从文件头开始算起)。有时候文件过大,可以只读前多少行做测试,节省时间。
之后可以打印少许数据查看数据集内容的结构。
数据读取与数据分析
赛题数据虽然是文本数据,每个新闻是不定长的,但任然使用csv格式进行存储。因此可以直接用Pandas
完成数据读取的操作。
十分钟快速入门 Pandas
一. 数据读取
赛题数据虽然是文本数据,每个新闻是不定长的,但任然使用csv格式进行存储。因此可以直接用Pandas
完成数据读取的操作。
import pandas as pd
train_df = pd.read_csv('../data/train_set.csv', sep='\t', nrows=100)
这里的read_csv
由三部分构成:
-
读取的文件路径,这里需要根据改成你本地的路径,可以使用相对路径或绝对路径;
-
分隔符
sep
,为每列分割的字符,设置为\t
即可,两列之间的竖线(分隔符) -
读取行数nrows,为此次读取文件的函数,是数值类型(由于数据集比较大,建议先设置为100)可以不设置(, nrows=100),即读取全部;
train_df.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… |
上图是读取好的数据,是表格的形式。第一列为新闻的类别,第二列为新闻的字符。
二. 数据分析
在读取完成数据集后,我们还可以对数据集进行数据分析的操作。虽然对于非结构数据并不需要做很多的数据分析,但通过数据分析还是可以找出一些规律的。
此步骤我们读取了所有的训练集数据,在此我们通过数据分析希望得出以下结论:
- 赛题数据中,新闻文本的长度是多少?
- 赛题数据的类别分布是怎么样的,哪些类别比较多?
- 赛题数据中,字符分布是怎么样的?
1. 句子长度分析
在赛题数据中每行句子的字符使用空格进行隔开,所以可以直接统计单词的个数来得到每个句子的长度。统计并如下:
train_df['text_len'] = train_df['text'].apply(lambda x: len(x.split(' ')))
#.apply 遍历,应用到每一行 all sentens
#lambda [arg1 [,arg2,.....argn]]:expression
print(train_df['text_len'].describe())
Populating the interactive namespace from numpy and matplotlib
count 100.000000 #100篇 text count 100
mean 872.320000 #mean 平均长
std 923.138191 #标准差std 923
min 64.000000
25% 359.500000
50% 598.000000
75% 1058.000000
max 7125.000000
Name: text_len, dtype: float64
对新闻句子的统计可以得出,本次赛题给定的文本比较长,每个句子平均由907个字符构成,最短的句子长度为2,最长的句子长度为57921。
PS:检测类型,基本信息
type(train_df) #pandas.core.frame.DataFrame 能用 info();describe()
type(train_df['text']) #pandas.core.series.Series ;describe()
train_df.columns #列名
train_df['text'].columns #不能跑,因为格式
train_df.info() # 显示数据类型 pandas.core.frame.DataFrame
<class 'pandas.core.frame.DataFrame'> 类型
RangeIndex: 100 entries, 0 to 99。 100篇
Data columns (total 3 columns):
label 100 non-null int64 100 篇没缺失
text 100 non-null object
text_len 100 non-null int64
dtypes: int64(2), object(1)
memory usage: 2.5+ KB
下图将句子长度绘制了直方图,可见大部分句子的长度都几种在2000以内。
#上面代码里的%pylab是ipython里的magic function。他其实相当于以下代码:
import numpy
import matplotlib
from matplotlib import pylab, mlab, pyplot
np = numpy
plt = pyplot
from IPython.display import display
from IPython.core.pylabtools import figsize, getfigs
from pylab import *
from numpy import *
# https://ipython.org/ipython-doc/2/api/generated/IPython.core.magics.pylab.html
#%pylab inline 在vscode里,加上#%%,其实就是 jupyter 模式
%pylab inline
_ = plt.hist(train_df['text_len'], bins=200)
#hist 直方图
#bins: 像素大小
plt.xlabel('Text char count')
plt.title("Histogram of char count")
Text(0.5, 1.0, 'Histogram of char count')
2. 新闻类别分布
接下来可以对数据集的类别进行分布统计,具体统计每类新闻的样本个数。
train_df['label'].value_counts().plot(kind='bar')
# 获取第一列。统计每个类别数量。绘图
plt.title('News class count')
plt.xlabel("category")
#不均匀会导致,-> 预测极端,不准确
Text(0.5, 0, 'category')
在数据集中标签的对应的关系如下:{‘科技’: 0, ‘股票’: 1, ‘体育’: 2, ‘娱乐’: 3, ‘时政’: 4, ‘社会’: 5, ‘教育’: 6, ‘财经’: 7, ‘家居’: 8, ‘游戏’: 9, ‘房产’: 10, ‘时尚’: 11, ‘彩票’: 12, ‘星座’: 13}
PS 方法2
pands的每一列领出来,是 series
用Series.value_counts()来获得各个类别出现的次数,再用seaborn.barplot()可视化出来。
#进阶版,比matplot。散点图
#与原图的区别
class_count = pd.DataFrame(data_train['label'].value_counts()).reset_index()
#sns seaborn https://blog.csdn.net/ice_martin/article/details/61617053
sns.barplot(x='index',y = 'label',data = class_count)
plt.title('News class count')
plt.ylabel("count")
plt.xlabel("category")
从统计结果可以看出,赛题的数据集类别分布存在较为不均匀的情况。在训练集中科技类新闻最多,其次是股票类新闻,最少的新闻是星座新闻。
3. 字符分布统计
接下来可以统计每个字符出现的次数,首先可以将训练集中所有的句子进行拼接进而划分为字符,并统计每个字符的个数。
从统计结果中可以看出,在训练集中总共包括6869个字,其中编号3750的字出现的次数最多,编号3133的字出现的次数最少。
from collections import Counter
#由于数据集过大,这里可以做了一下采样
#data_train_sample = data_train.sample(n=10000,random_state=405633) #作用10000,随机种子
#首先可以将训练集中所有的句子进行拼接进而划分为字符
all_lines = ' '.join(list(train_df['text']))
#' '.join 连接两个字符
#list 转换类型,类似 int()
word_count = Counter(all_lines.split(" "))
word_count = sorted(word_count.items(), key=lambda d:d[1], reverse = True)
'''
iterable -- 可迭代对象。
cmp -- 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
'''
print(len(word_count)) #100篇 文章所有不重复的字符
print(word_count[0]) # 从第一开始
print(word_count[-1])# index 屁股后头开始数第一个 最小值
2405 #100篇 文章所有不重复的字符
('3750', 3702)
('5034', 1)
PS:
a = word_count.items() #
for x in a:
#print(type(x)) 输出元祖
# print(x)
# print(x[1]) 输出 词频
这里还可以根据字在每个句子的出现情况,反推出标点符号。下面代码统计了不同字符在句子中出现的次数,其中字符3750,字符900和字符648在20w新闻的覆盖率接近99%,很有可能是标点符号。
from collections import Counter
train_df['text_unique'] = train_df['text'].apply(lambda x: ' '.join(list(set(x.split(' ')))))
#集合里面的元素不能重复,类似于列表。文章·每行·所有单词连接起来
all_lines = ' '.join(list(train_df['text_unique']))
#所有行单词串联,100字符串->1str
word_count = Counter(all_lines.split(" "))
#每个单词出现的次数
word_count = sorted(word_count.items(), key=lambda d:int(d[1]), reverse = True)
#d[1]=第二例,词频
print(word_count[0])
print(word_count[1])
print(word_count[2])
('900', 99) #99 是数
('3750', 99)
('648', 96)
任务
- 假设字符3750,字符900和字符648是句子的标点符号,请分析赛题每篇新闻平均由多少个句子构成?
- 统计每类新闻中出现次数最多的字符
解答:
假设字符3750,字符900和字符648是句子的标点符号,但分隔每个句子都是通过句号,所以我们得先找出句号。一般而言,句号出现在句子末尾,那么每个文本的最后一个字符就应该是句号。我们可以检测一下:
data_train_sample['last_word'] = data_train_sample['text'].apply(lambda x: x.split(' ')[-1])
# 取每个句子【text】的 。,sers.apply
data_train_sample['last_word'].value_counts()
#4172(10000)
900 4172
2662 2023
885 741
1635 370
2465 350
...
3944 1
7042 1
4744 1
2484 1
4180 1
可以看到在文本最后一个字符里,“900”出现次数最多,占接近一半。所以这里假设“900”为句号。
data_train_sample['sent_len'] = data_train_sample['text'].apply(lambda x: len(x.split('900'))) #用句号去分离每片文章,得出每片文章的句数
sent_len | |
---|---|
count | 10000.000000 |
mean | 17.253200 |
std | 19.339811 |
min | 1.000000 |
25% | 7.000000 |
50% | 12.000000 |
75% | 22.000000 |
max | 670.000000 |
从上表得出这10000个样本文本的句子平均长度为17,最短为1,最长为670。
如果要这三个标点都能作为分隔符的话,python里字符串的split只限单个分隔符对句子进行分隔,而re模块里的可以实现多个分隔符。
import re #模块reluation
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 |
类别内字符统计
# pandas 基础 https://blog.csdn.net/zhangchuang601/article/details/79583551
# pandas 选取特定的行;列
for i in range(14): #jupyter
data_class = data_train_sample[data_train_sample['label']==i] #datafram,为0 的所有文章
all_lines = ' '.join(list(data_class['text'])) #同一个类的新闻,用空隔连接起来
word_count = Counter(all_lines.split(" ")) #counter
word_count = sorted(word_count.items(), key=lambda d:d[1], reverse = True)
print("类别",i,":",word_count[:5])
#pandas.groupby() 对数据进行聚合
同理可以使用pandas.groupby()
数据分析的结论
通过上述分析我们可以得出以下结论:
- 赛题中每个新闻包含的字符个数平均为1000个,还有一些新闻字符较长;
- 赛题中新闻类别分布不均匀,科技类新闻样本量接近4w,星座类新闻样本量不到1k;
- 赛题总共包括7000-8000个字符;
通过数据分析,我们还可以得出以下结论:
- 每个新闻平均字符个数较多,可能需要截断;
- 由于类别不均衡,会严重影响模型的精度;
类别 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)]
可以看出每个类的出现最多的几个字符都是相似的,所以基本上就是标点或者暂停词了。
本章小结
本章对赛题数据进行读取,并新闻句子长度、类别和字符进行了可视化分析。