NLP知识点:文本数据的预处理

「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」。

1. 为啥要有预处理?

NLP(Natural Language Processing)指的是自然语言处理,就是研究计算机理解人类语言的一项技术。

要研究语言处理,那么首先得有语言文本。之前讲过利用Tokenizer分词器对固定格式的文本进行序列化处理。

在序列化之前,如何把这些文本按照一定的格式处理好,也是一项基础的工作,这一步叫数据的预处理。

2. 文本存储的几种载体

一般直接可读的文本数据会存储在这么几种文件里:

  • 数据库:sqlite、mysql……
  • 表格文件:csv、excel……
  • 纯文本文件:txt、json……

下面我们就挨个来讲一下,如何读写这些文件。

2.1 数据库

数据库存储文本信息,有很多优势:

  • 支持大数据量存储
  • 查询高效
  • 支持复杂的关联关系

拿sqlite数据库举例子,看一下如何进行数据的读取。

下面有一个数据库文件,大小为8KB。

db图标.png

数据库里面有一张名称叫"ci"的表,数据结构和内容如下:

db内容.png

表里面存储了25条数据,是25篇宋词,每条数据包含序号(value)、词牌名(rhythmic)、作者(author)、内容(content)。

假设,我们要使用每首词的内容作为训练集,那么我们该如何组织数据呢?

# 导入sqlite的支持
import sqlite3

# 保存每首词的内容
str_array  = []

# 指定文件位置(同级目录data下的data.db文件)建立连接
conn = sqlite3.connect('./data/data.db')
# 执行查询语句,只获取author, content两个字段,获得游标(内含结果)
cursor = conn.execute("SELECT author, content from ci;")
# 循环结果
for row in cursor:
    # 获取结果(0 author, 1 content)中索引为1的数据
    ci = row[1]
    # 加入内容列表
    str_array.append(ci)

# 关闭操作
cursor.close()
conn.close()

# 打印结果
print(str_array)
复制代码

最终打印结果如下:

[
'出砒霜,价钱可。\r\n赢得拨灰兼弄火。\r\n畅杀我。', 
'丞相有才裨造化,圣皇宽诏养疏顽。\r\n赢取十年闲。', 
'归。\r\n十万人家儿样啼。\r\n公归去,何日是来时。', 
'归。\r\n数得宣麻拜相时。\r\n秋前後,公衮更莱衣。', 
'百尺长藤垂到地,千株乔木密参天。\r\n只在郡城边。', 
'巍峨万丈与天高。\r\n物轻人意重,千里送鹅毛。\r\n\r\n'
 ……
]
复制代码

其中\r\n是回车换行。我们看看巍峨万丈与天高。\r\n物轻人意重,千里送鹅毛。\r\n\r\n在文本框的展示,能够帮助你更好地理解。

rn文本框展示.gif

延伸知识:sqlite3的写入

数据的写入很简单,和数据读取类似。也是先建立连接,然后执行sql语句,这里多一个commit提交,最后断开连接。

下面举例说明,连续插入2条数据。

# 导入sqlite的支持
import sqlite3

# %% 数据表数据的写入
conn = sqlite3.connect('./data/data.db')
for t in[(9998,"名称1","作者1","正文1"),(9999,"名称2","作者2","正文2")]:
    conn.execute("insert into ci values (?,?,?,?)", t)
    
conn.commit()
conn.close()
复制代码

2.2 表格文档

相比于数据库,表格类文档(csv、excel)也是一种很好的文本存储方式。

它双击就能打开,可以直接操作内容,也能利用自带的工具做一些数据处理。

下面有一个csv文件,里面有很多行,每一行是一首宋词,前三列分别是:词牌名、作者、内容。

csv内容.png

假设,我们要使用每首词的内容作为训练集,那么我们该如何组织数据呢?

import csv

# 建立存储内容的数组
str_array  = []

# 构建阅读器,指定文件位置(同级目录data下的data.csv文件),指定编码格式
csv_reader = csv.reader(open("./data/data.csv",encoding="gbk"))
# 循环每一行
for row in csv_reader:
    # 取出索引为2的列(第3列),存入数组
    str_array.append(row[2])
	
# 打印数据    
print(str_array)
复制代码

最终打印结果如下:

[
'出砒霜,价钱可。\r\n赢得拨灰兼弄火。\r\n畅杀我。', 
'丞相有才裨造化,圣皇宽诏养疏顽。\r\n赢取十年闲。', 
'归。\r\n十万人家儿样啼。\r\n公归去,何日是来时。', 
'归。\r\n数得宣麻拜相时。\r\n秋前後,公衮更莱衣。', 
'百尺长藤垂到地,千株乔木密参天。\r\n只在郡城边。', 
'巍峨万丈与天高。\r\n物轻人意重,千里送鹅毛。\r\n\r\n'
 ……
]
复制代码

这样,这个数组数据就可以使用了。

延伸知识:csv的写入

数据的写入和数据读取类似。先构建一个写入器 ,写入数据,最后需要关闭打开的文件。

下面举例说明,新建一个csv文件,然后插入1条数据。

import csv

# 以写入的方式打开(新建)一个文件,指定编码
f_csv = open('./data/data2.csv','w',encoding='gbk', newline='')
# 获取这个文件的写入器
csv_writer = csv.writer(f_csv)
# 写入一行数据
csv_writer.writerow(['第一列','第二列','第三列'])

# 关闭文件
f_csv.close()
复制代码

代码执行后,会在同级的data目录下新建一个data2.csv文件,然后写入一行3列的数据。

csv写入.png

2.3 文本文档

文本文档(txt)是最轻量级的一种文本存储方式。

它不像数据库或者表格文件那样有关联关系,它只能罗列一段段文本,它也无法承载太多的数据,一般上万行文本就会导致它读取困难。

但是,它也是有优势的。那就是——使用方便。

打开文件往里面输入字符就可以了。

因为没有行列条数的概念,一般文本文档要存储数据集,都是以特殊字符作为区分,例如回车换行符,一行就是一条数据。

下面有一段文本,我们看看如何读取它。

txt内容.png

import os

# 保存每行文本内容的数组
str_array  = []

# 指定文件位置(同级目录data下的data.txt文件)
f_read = open('./data/data.txt','rb')
# 读取一行文件
line = f_read.readline()
# 如果此行存在
while line: 
    # 读出内容
    wstr = str(line, encoding = "utf-8-sig")
    # 添加到数组
    str_array.append(wstr)
    # 接着再读下一行
    line = f_read.readline()

# 关闭文件
f_read.close()

# 打印数组
print(str_array)
复制代码

最终打印结果如下:

[
'巍峨万丈与天高。物轻人意重,千里送鹅毛。\r\n', 
'远来犹自忆梁陈。江南无好物,聊赠一枝春。\r\n', 
'用心勤苦是新诗。吟安一个字,拈断数茎髭。\r\n', 
'扪窗摸户入房来。笙歌归院落,灯火下楼台。\r\n', 
'酥某露出白皑皑。遥知不是雪,为有暗香来。\r\n', 
'称觞喜对二阳临。况当弦月上,一醉祝千春。\r\n'
]
复制代码

延伸知识:txt的写入

数据的写入和数据读取类似。先打开一个文件 ,写入数据,最后需要关闭打开的文件。

下面举例说明,新建一个txt文件,然后写入文本。

import os

# 以写入的方式打开(新建)data2.txt的文本
f_write = open('./data/data2.txt','w')
f_write.write('写入文本第一行\n第二行')

f_write.close()
复制代码

3. 组合拳:清洗数据并存为json文件

假设我们要训练一套关于宋词的数据,数据源就是下面数据库里的这张表的数据。

组合拳 词.png

看似这些数据井井有条,其实并不是那么完美。

组合拳 词的问题.png

此时你有几个诉求:

  1. 去冗余:去掉头尾多余的数据,去掉重复的数据。
  2. 做筛选:只想要《临仙江》这种句式的数据。
  3. 换存储:因为数据量不大,想以json文本格式存储清洗后的数据。

分析:

  1. 去掉文本的头尾空格和换行,可以使用strip()方法。去除重复数据,可通过代码逻辑实现,将遇到的句子保存起来,下一个句子到已保存列表里面查找,能查到说明重复,查不到说明第一次见。

  2. 《临仙江》的格式为:{[7个汉字]。<换行回车>[5个汉字],[5个汉字]。},可以通过正则匹配筛选出,正则表达式为:^[\u4e00-\u9fa5]{7}。\r\n[\u4e00-\u9fa5]{5},[\u4e00-\u9fa5]{5}。$

  3. 从数据库读取数据,筛选到合适的文本,组成json字符串写入文本即可。

代码如下:

import sqlite3
import json
import re

# 存储内容的数组
str_array = []
# 已经遇到过的内容
keys = {""}

# 筛选 {7。5,5。}格式内容的正则表达式
pattern = re.compile(r'^[\u4e00-\u9fa5]{7}。\r\n[\u4e00-\u9fa5]{5},[\u4e00-\u9fa5]{5}。$') 

# 连接数据库
conn = sqlite3.connect('./data/data.db')
# 执行查询,只获取内容字段,获得游标结果
cursor = conn.execute("SELECT content from ci;")
# 循环结果
for row in cursor:
    # 获取索引为0的列
    ci = row[0]
    # 裁剪头尾
    ci = ci.strip()
    # 匹配格式
    m = pattern.match(ci)
    # 没有匹配到
    if m == None:
        print('\n没有匹配到:',ci)
    else: # 匹配到
        print('\n匹配成功:',ci)
        # 是否出现过
        if ci in keys:
            # 出现过,是重复的,不处理
            print('\n已存在->',ci)
        else:
            # 没有出现过,加入出现列表,加入内容列表
            keys.add(ci)
            str_array.append(ci)

# 关闭游标和链接
cursor.close()
conn.close()

# 将内容列表转为json
j_str = json.dumps(str_array, indent=2, ensure_ascii=False)
# 打开(新建)文本
f_write = open('./data/data2.json','w')
# 写入文本
f_write.write(j_str)
# 关闭文件
f_write.close()
复制代码

生成的data2.json内容如下:

生成的json.png

猜你喜欢

转载自juejin.im/post/7029165733598724103
今日推荐