https://storage.googleapis.com/learning-datasets/binary-emotion.csvfrom bs4 import BeautifulSoup
import string
stopwords = ["a", "about", "above", "after", "again", "against", "all", "am", "an", "and", "any", "are", "as", "at","be", "because", "been", "before", "being", "below", "between", "both", "but", "by", "could", "did", "do",
"does", "doing", "down", "during", "each", "few", "for", "from", "further", "had", "has", "have", "having",
"he", "hed", "hes", "her", "here", "heres", "hers", "herself", "him", "himself", "his", "how",
"hows", "i", "id", "ill", "im", "ive", "if", "in", "into", "is", "it", "its", "itself",
"lets", "me", "more", "most", "my", "myself", "nor", "of", "on", "once", "only", "or", "other", "ought",
"our", "ours", "ourselves", "out", "over", "own", "same", "she", "shed", "shell", "shes", "should",
"so", "some", "such", "than", "that", "thats", "the", "their", "theirs", "them", "themselves", "then",
"there", "theres", "these", "they", "theyd", "theyll", "theyre", "theyve", "this", "those", "through",
"to", "too", "under", "until", "up", "very", "was", "we", "wed", "well", "were", "weve", "were",
"what", "whats", "when", "whens", "where", "wheres", "which", "while", "who", "whos", "whom", "why",
"whys", "with", "would", "you", "youd", "youll", "youre", "youve", "your", "yours", "yourself","yourselves"]
table = str.maketrans('', '', string.punctuation)下面我将为你详细解释这段Python代码的含义、实现原理、用途以及注意事项。
### 一、 代码整体含义
这段代码主要完成了两个准备工作:
1. 定义了一个包含大量英语停用词的列表 stopwords。
2. 创建了一个用于删除字符串中所有标点符号的字符映射转换表 table。
这通常是**自然语言处理(NLP)**或**文本数据预处理**阶段的前两步核心代码,常见于文本分类、情感分析、词频统计等任务之前。
---
### 二、 分段详细解释
#### 1. stopwords 列表
```python
stopwords = ["a", "about", "above", ...]
```
* 含义:这是一个包含了常见英语停用词的列表。
* 什么是停用词:停用词是指在文本中频繁出现,但对文本核心语义贡献极小的词汇。例如 "a", "the", "is", "and" 等。它们主要起语法连接作用,本身没有太多实际意义。
* 用途:在进行文本分析时,这些词会引入大量噪音,增加计算量且干扰模型提取关键特征。因此,通常会在预处理阶段将它们过滤掉。
#### 2. table = str.maketrans('', '', string.punctuation)
这行代码非常精妙,它利用了Python内置字符串方法 maketrans 来构建一个翻译/映射表。
* *string.punctuation**:这是 string 模块中的常量,包含了所有的英文标点符号,值为 '!"#$%&\'()*+,-./:;<=>?@[\\]^_{|}~'`。
* *str.maketrans(x, y, z)**:当传入三个参数时:
* 第一个参数 x:要替换的字符(这里为空字符串 '')。
* 第二个参数 y:替换成的字符(这里为空字符串 '',注意 x 和 y 长度必须一致,因为它们是一一对应的,这里长度都是0)。
* 第三个参数 z:要删除的字符。
* 实现原理:这行代码生成了一个字符映射字典(Translation Table),该字典指定了:将 string.punctuation 中的每一个标点符号映射为 None(即删除)。
* 用途:配合 str.translate() 方法,可以极速、批量地清除文本中的所有标点符号。
---
### 三、 实际应用场景(如何使用)
这段代码只是定义了工具,通常它们会配合以下方式使用:
```python
import string
from bs4 import BeautifulSoup
# ...(此处为你的代码:定义 stopwords 和 table)...
# 假设我们获取到了一段带有HTML标签和标点符号的脏文本
raw_html = "<p>Hello, world! This is a test for NLP preprocessing.</p>"
# 1. 使用 BeautifulSoup 去除 HTML 标签,提取纯文本
soup = BeautifulSoup(raw_html, 'html.parser')
clean_text = soup.get_text()
# 结果: "Hello, world! This is a test for NLP preprocessing."
# 2. 使用 table 去除标点符号
no_punct_text = clean_text.translate(table)
# 结果: "Hello world This is a test for NLP preprocessing"
# 3. 转换为小写并按空格切分
words = no_punct_text.lower().split()
# 结果: ['hello', 'world', 'this', 'is', 'a', 'test', 'for', 'nlp', 'preprocessing']
# 4. 过滤停用词
final_words = [word for word in words if word not in stopwords]
# 结果: ['hello', 'world', 'test', 'nlp', 'preprocessing']
```
注:代码中引入了 BeautifulSoup,说明这段代码的上下文很可能是用于解析网页抓取下来的数据,先用 BeautifulSoup 剥离 HTML 标签,再进行上述的文本清洗。
---
### 四、 注意事项与优化建议
1. 性能优化(重要):
在过滤停用词时,代码 if word not in stopwords 中的 stopwords 是一个**列表**。在Python中,列表的 in 操作时间复杂度是 $O(N)$。如果停用词很多或文本很大,这会非常慢。
建议:将 stopwords 转换为**集合**,集合的 in 操作时间复杂度是 $O(1)$,查找速度极快。
```python
stopwords_set = set(stopwords) # 初始化时转为集合
final_words = [word for word in words if word not in stopwords_set]
```
2. 停用词的适用性:
代码中的停用词表是标准的英文停用词。如果你处理的是中文、其他语言,或者特定领域的文本(如医学、法律),这个列表是不够的,需要根据业务场景自定义停用词表。
3. 标点符号处理的边界情况:
string.punctuation 只包含标准的 ASCII 英文标点符号。如果你的文本中包含中文标点(如 ,。!?)、省略号 …、特殊的 Unicode 引号(如 “”),它们将无法被 table 删除。对于多语言文本,可能需要使用正则表达式(如 re.sub(r'[^\w\s]', '', text))来移除所有非单词非空白字符。
4. 大小写问题:
在比对停用词之前,务必确保文本已经被转换为**小写**.lower())。因为停用词表里都是小写,如果文本是 "The",它就不会被识别为停用词而被错误保留。
import csv
sentences=[]
labels=[]
with open('binary-emotion.csv', encoding='UTF-8') as csvfile:
reader = csv.reader(csvfile, delimiter=",")
for row in reader:
labels.append(int(row[0]))
sentence = row[1].lower()
sentence = sentence.replace(",", " , ")
sentence = sentence.replace(".", " . ")
sentence = sentence.replace("-", " - ")
sentence = sentence.replace("/", " / ")
soup = BeautifulSoup(sentence)
sentence = soup.get_text()
words = sentence.split()
filtered_sentence = ""
for word in words:
word = word.translate(table)
if word not in stopwords:
filtered_sentence = filtered_sentence + word + " "
sentences.append(filtered_sentence)下面我将为你详细解释这段Python代码的含义、实现原理、用途以及需要注意的事项。
### 一、 代码含义与用途
这段代码的主要用途是**自然语言处理(NLP)的数据预处理**。它从一个名为 binary-emotion.csv 的CSV文件中读取文本和标签数据,对文本进行一系列的清洗和标准化操作(如大小写转换、标点符号处理、去除HTML标签、去除停用词等),最终将处理后的干净文本和对应的情感标签分别存储在 sentences 和 labels 两个列表中。
从文件名 binary-emotion.csv 和标签提取方式来看,是一个**二分类情感分析**(如正面/负面,或积极/消极)的数据集准备阶段。
### 二、 实现原理(逐行解析)
1. *import csv**
导入Python内置的CSV模块,用于读取逗号分隔值的文件。
2. *sentences=[] / labels=[]**
初始化两个空列表,用于分别存放预处理后的句子文本和对应的情感标签。
3. *with open('binary-emotion.csv', encoding='UTF-8') as csvfile:**
以只读模式打开当前目录下的 binary-emotion.csv 文件,指定编码为UTF-8,使用 with 语句确保操作完成后文件会自动关闭。
4. *reader = csv.reader(csvfile, delimiter=",")**
创建一个CSV读取器,指定列与列之间的分隔符为逗号 ,。
5. *for row in reader:**
遍历CSV文件中的每一行数据,每一行 row 是一个列表,包含该行的各列内容。
6. *labels.append(int(row[0]))**
将每行的第一列(索引0)转换为整数类型,并添加到 labels 列表中。这通常是文本的分类标签(如0代表消极,1代表积极)。
7. *sentence = row[1].lower()**
提取每行的第二列(索引1)作为句子文本,并使用 .lower() 将所有字符转换为小写。这是NLP的常见操作,可以减少词汇量(使"Apple"和"apple"被视为同一个词)。
8. 标点符号两边加空格:
```python
sentence = sentence.replace(",", " , ")
sentence = sentence.replace(".", " . ")
sentence = sentence.replace("-", " - ")
sentence = sentence.replace("/", " / ")
```
将特定的标点符号(逗号、句号、连字符、斜杠)替换为“空格+标点+空格”。**原理**:在后续使用 split() 切分句子时,如果标点紧贴着单词(如"apple,"),它们会被切分成一个整体["apple,"];加上空格后,标点就会被切分成独立的元素["apple", ","],这有助于在词汇层面对标点进行单独处理或过滤。
9. *soup = BeautifulSoup(sentence) / sentence = soup.get_text()**
使用 BeautifulSoup 库解析句子,并提取纯文本。**原理**:原始文本中可能包含HTML标签(如 <br>, <p> 等)get_text() 方法可以剥离所有HTML标签,只保留文字内容。
10. *words = sentence.split()**
使用空格将句子切分成单词列表。
11. 过滤停用词与特殊字符:
```python
filtered_sentence = ""
for word in words:
word = word.translate(table)
if word not in stopwords:
filtered_sentence = filtered_sentence + word + " "
```
- word.translate(table):使用字符串的 translate 方法,根据映射表 table(通常用于删除或替换特定字符,如去除特殊标点或数字)处理单词。
- if word not in stopwords:检查处理后的单词是否在停用词表 stopwords 中(停用词如 "the", "a", "is" 等,对情感判断无实际帮助且高频出现)。
- 如果单词不在停用词表中,则将其拼接到 filtered_sentence 中,单词间用空格分隔。
12. *sentences.append(filtered_sentence)**
将最终清洗完毕的句子添加到 sentences 列表中。
### 三、 注意事项与潜在问题
1. 缺失的导入和变量定义:
代码中使用了 BeautifulSouptable 和 stopwords,但在代码片段中并未定义。在实际运行前,必须补充:
- 导入 BeautifulSoupfrom bs4 import BeautifulSoup
- 定义 table:通常使用 str.maketrans('', '', string.punctuation) 来去除所有标点。
- 定义 stopwords:通常是一个包含常见停用词的集合set),如 stopwords = set(['the', 'a', 'an', ...])。
2. 字符串拼接效率低:
在循环中使用 filtered_sentence = filtered_sentence + word + " " 进行字符串拼接,在Python中由于字符串是不可变对象,每次拼接都会创建新对象,当数据量很大时效率极低。
优化建议:使用列表的 append 方法,最后用 " ".join() 合并:
```python
filtered_words = []
for word in words:
word = word.translate(table)
if word not in stopwords:
filtered_words.append(word)
filtered_sentence = " ".join(filtered_words)
```
3. 表头处理缺失:
如果CSV文件的第一行是表头(如 "label, sentence"),代码会将表头也作为数据处理int(row[0]) 会报错)。建议使用 next(reader) 跳过表头,或者使用 csv.DictReader。
4. 标点处理的逻辑冲突:
代码前半部分特意给逗号、句号等加了空格,使其成为独立的 token。但后半部分又使用了 word.translate(table)(通常table是用来消除标点的)和停用词过滤。如果 table 删除了所有标点,那么前面加空格的步骤就变成了无用功;如果目的是保留标点作为特征,那么 table 的映射规则需要谨慎编写。
5. 缺少异常处理:
如果某一行数据格式不正确(例如只有一列,导致 row[1] 抛出 IndexError),或者标签不是数字(导致 int(row[0]) 抛出 ValueError),程序会直接崩溃。建议添加 try-except 块来跳过或记录异常数据。
如果你有关于这段代码更具体的疑问,或者需要补充完整的预处理代码,随时可以告诉我!
from collections import Counter
from bs4 import BeautifulSoup
# 假设停用词列表已定义
stopwords = ["a", "an", "the", "is", "in", "and", "of", "to", "it"] # 示例截断
# Tokenize 函数保持不变
def tokenize(text):
soup = BeautifulSoup(text, "html.parser")
cleaned_text = soup.get_text()
tokens = cleaned_text.lower().split()
filtered_tokens = [token for token in tokens if token not in stopwords]
return filtered_tokens
def build_vocab(sentences):
counter = Counter()
for text in sentences:
counter.update(tokenize(text))
sorted_words = sorted(counter.items(), key=lambda x: x[1], reverse=True)
vocab = {word: idx + 1 for idx, (word, _) in enumerate(sorted_words)}
vocab['<pad>'] = 0
return vocab, sorted_words # 🔥 修改:同时返回排序好的词频列表,方便后续打印
# 假设这是你的训练数据
training_sentences = [
"This is a great movie!",
"This film is terrible and boring.",
"I love this movie, it is great."
]
# 🔥 修改:接收返回的排序列表
vocab, sorted_words = build_vocab(training_sentences)
# ==========================================
# 美观打印区域
# ==========================================
print(f"📊 词汇表总大小: {len(vocab)} 个词\n")
# -----------
# 方式一:查看高频词和低频词(最推荐,快速了解数据分布)
# -----------
print("🏆 【Top 10 高频词】(索引越小,频次越高):")
print("-" * 35)
for word, freq in sorted_words[:10]:
idx = vocab[word]
# 使用 f-string 格式化对齐:{idx:<5} 表示左对齐占5个字符宽度
print(f" 索引: {idx:<5} 词频: {freq:<5} 词: '{word}'")
print("\n...\n")
print("📉 【Top 10 低频词】(出现次数最少的词):")
print("-" * 35)
for word, freq in sorted_words[-10:]:
idx = vocab[word]
print(f" 索引: {idx:<5} 词频: {freq:<5} 词: '{word}'")
# -----------
# 方式二:分段打印(适合查看特定范围的索引映射,像字典一样)
# -----------
print("\n📖 【索引 1~10 映射详情】(每行3个):")
print("-" * 50)
# 取出索引 1 到 10 的词 (跳过0的<pad>)
items_to_show = list(vocab.items())[1:11]
for i in range(0, len(items_to_show), 3):
chunk = items_to_show[i:i+3]
# 生成类似 " 'great': 1 | 'movie': 2 | 'terrible': 3 " 的格式
line = " | ".join([f"'{word}': {idx}" for word, idx in chunk])
print(f" {line}")
# -----------
# 方式三:查找特定词汇的索引(最实用的调试方法)
# -----------
test_words = ['great', 'movie', 'boring', 'awesome', '<pad>']
print("\n🔍 【特定词汇查询】:")
print("-" * 30)
for word in test_words:
# 使用 get 方法,如果找不到返回 '未收录' 标识
idx = vocab.get(word, "未收录(UNK)")
print(f" '{word}' -> 索引: {idx}")这段Python代码的主要功能是**构建自然语言处理(NLP)任务中的词汇表,并以多种美观的方式对词汇表进行统计和打印展示**。
下面我将从实现原理、用途和注意事项三个方面为您详细解释:
### 一、 实现原理
代码的整体执行流程分为三个阶段:文本预处理与分词、词频统计与词汇表构建、格式化打印。
1. **文本预处理与分词 tokenize 函数)**:
- **去除HTML标签**:使用 BeautifulSoup 将可能含有HTML标签的文本解析为纯文本。
- **统一小写**:将文本全部转为小写,避免 "Great" 和 "great" 被识别为两个不同的词。
- **简单分词**:使用 Python 内置的 split() 方法按空格切分文本。
- **去除停用词**:通过列表推导式过滤掉 "a", "is", "in" 等对文本语义贡献不大但频率极高的词。
2. **构建词汇表 build_vocab 函数)**:
- **词频统计**:使用 collections.Counter 遍历所有句子,统计每个词的出现次数。
- **按频排序**:将统计结果按词频从高到低排序。高频词排在前面,这在NLP中很常见,可以使得常用词的索引较小,有助于模型收敛和嵌入层压缩。
- **生成映射字典**:通过字典推导式,为排序后的词分配从 1 开始的递增整数索引。
- **添加占位符**:将 <pad>(填充符)的索引硬编码为 0。在批处理训练时,不同长度的句子需要补齐到相同长度0 通常作为填充位置的索引。
3. **美观打印展示**:
- **方式一(高低频极值查看)**:通过切片 [:10] 和 [-10:] 快速查看数据分布的头部和尾部,使用 f-string 的 :<5 语法实现左对齐和固定宽度,使输出像表格一样整齐。
- **方式二(分段映射查看)**:将字典转为列表后切片,每次循环步长为3,实现每行打印3个键值对的排版。
- **方式三(特定词汇查询)**:使用字典的 get(key, default) 方法安全查询,避免词汇不存在时抛出 KeyError 异常。
---
### 二、 用途
1. **NLP模型的数据预处理基础**:在构建文本分类、情感分析、机器翻译等模型前,必须将文本转换为数字。这段代码生成的 vocab 字典就是“词到索引”的映射表,是后续词嵌入(Word Embedding)层的基石。
2. **数据探索与质量检查(EDA)**:通过查看高频词和低频词,可以快速发现数据集的特点或问题。例如:如果高频词中包含大量无意义词汇,说明停用词表不够完善;如果低频词全是错别字,则可能需要进行拼写纠正或字符级过滤。
3. **调试与日志记录**:在训练模型时,打印特定词汇的索引或局部映射,有助于开发者验证数据管道是否正确,确认 <pad> 等特殊标记是否被正确处理。
---
### 三、 注意事项
1. **分词方式的局限性**:
- 代码使用的是 str.split(),这只能按空格切分。对于英文,它无法处理标点符号(如 "movie!" 会被切分为 "movie!" 而不是 "movie" 和 "!");对于中文等非空格分隔的语言,此方法完全失效。**改进建议**:使用 nltk.word_tokenize 或 spaCy 等专业分词工具。
2. **停用词表过于简单**:
- 示例中的 stopwords 仅有9个词,实际应用中需要更完整的停用词表。同时,标点符号(如 !, ,, .)并没有在过滤逻辑中被剔除,它们会被当作普通词计入词汇表。
3. **未处理未知词**:
- 词汇表只包含了训练集里出现过的词。在测试或推理阶段,如果遇到新词,代码中用 "未收录(UNK)" 字符串代替了索引。但在实际送入模型时,必须为其分配一个特定的整数索引(通常为 1,即 <unk> 标记)。**改进建议**:在 build_vocab 中添加 vocab['<unk>'] = 1,并将其他词的索引顺延,查询时使用 vocab.get(word, 1)。
4. **字典遍历顺序的依赖**:
- 在“方式二”的代码 list(vocab.items())[1:11] 中,默认了字典遍历顺序与插入顺序一致。这在 Python 3.7+ 中是成立的(字典保持插入顺序),但如果在更老的 Python 版本中运行,输出顺序将是随机的。
5. **低频词切片的边界情况**:
- 当词汇表总大小小于10时sorted_words[:10] 和 sorted_words[-10:] 会有重叠,打印时可能会看到相同的词出现在高频和低频区域。