答疑解惑 | csv 数据字段错位?导入 Stata 中途报错?到底怎么解决……

更多详情请点击查看原文:答疑解惑 | csv 数据字段错位?导入 Stata 中途报错?到底怎么解决……

Python教学专栏,旨在为初学者提供系统、全面的Python编程学习体验。通过逐步讲解Python基础语言和编程逻辑,结合实操案例,让小白也能轻松搞懂Python!
>>>点击此处查看往期Python教学内容

本文目录

一、引言

二、字段错位的根本原因是什么?

三、解决方法

  情形1:你的 csv 文件中存在封闭符

  情形2:你的 csv 文件中不存在封闭符

    (1)文件受异常分隔符影响
    (2)文件受异常换行符影响

四、csv 文件的其他注意事项

五、结束语

六、相关推荐

本文共 5462 个字,阅读大约需要 14 分钟,欢迎指正!

一、引言

经常与数据打交道的朋友可能都有过这样的经历:自己的数据偶尔会出现字段错位,或者使用 Stata 等工具导入数据时报错。以上情形多发生在 csv 文件身上。本期文章我们就来详细说明一下出现这种问题的主要原因以及使用 Python 解决这类问题的方法。

二、字段错位的根本原因是什么?

先说答案:主要原因是这些 csv 文件中的单元格数据值中夹带了一些换行符或者逗号(分隔符)。

csv 是一种以纯文本内容存储表格数据的文件类型,可以被 Excel / WPS 等办公软件直接打开,csv 文件中的一行就代表着表格数据中的一行(多数情况下第一行为字段名),一行中的多个数据值使用特定符号进行分隔,常见的分隔符有英文逗号(,)、制表符\t等。多行表格数据就对应着文件中的多行文本内容,这意味着每两行数据之间(一行数据的最后面)都存在一个换行符\n,如下图所示。

图片

使用 VSCode 并借助扩展“Rainbow CSV”预览 csv 文件的截图

通过以上描述,我们知道了 csv 文件中存在着两个用以支撑文件结构的特殊字符——分隔符换行符,其中分隔符一般情况下为英文逗号(,)。这意味着,如果 csv 文件中出现了意外的分隔符和换行符(即数据值中出现的逗号或者换行符),那么一些工具(例如 Stata)在导入数据时可能就会出现解析异常甚至报错。

图片

图示:单元格数据值存在异常换行符和异常分隔符的 csv 数据

三、解决方法

在讨论解决方法之前,有一个问题比较关键——数据的来源。这里的“来源”指的是你的 csv 数据是用什么工具生成/写入/导出的。为什么要说这一点呢,因为不同工具在 csv 文件的读写方面可能会存在些许差异。笔者在使用 Python 的 Pandas 库、csv 库以及 Oracle 数据库导出数据时(Pandas 1.5 版本和 2.x 版本均已测试,所有工具均使用默认导出方式)发现,如果某个单元格中出现了分隔符(,)换行符(\n),那么导出后的文件中该单元格数据值都会使用封闭符(")包裹起来,这样做就是为了方便存储带有特殊符号的数据,同时在导入数据时也能完整地识别单元格,避免问题出现。不过笔者在使用 Stata 17的export delimited命令导出 csv 文件时发现,它并不会自动为任何单元格数据添加封闭符,需要主动设置quoto选项后才可以。

图片

图示封闭符:单元格数据值两端的英文双引号

上述情况说明,如果你需要使用 Python 去导入/读取一个由 Python 或 Oracle 数据库导出的 csv 文件,那么大概率不会出现本文标题中陈述的问题。但如果你需要用 Stata 来导入一个“有问题的” csv 数据,那么就极有可能遭遇警告或报错。因此,这个问题可以分为两种情形进行讨论。

图片

*图片与正文内容无关

情形1:你的 csv 文件中存在封闭符

这说明数据本身没有问题,只是在使用 Stata 进行导入时,Stata 没有严格地解析封闭符,那么我们在使用 Stata 的import delimited命令导入数据时只需要添加bindquote(strict)选项就可以正确地进行导入,以下是一个示范命令:

// Stata 代码
// 使用下面代码的前提是你的 csv 文件第一行是表头
import delimited "C:\FILE.csv", varnames(1) clear bindquote(strict)

与此同时,如果你还需要使用 Stata 导出 csv 数据,那么建议在导出时为 csv 数据添加封闭符,避免后续导入时出现异常问题。导出时可以使用下面的命令:

// Stata 代码
export delimited using "C:\FILE_SAVE.csv", delimiter(",") quote replace

另外,如果你想对数据进行一次彻底清洗,去除或者替换掉多余的分隔符(,)换行符(\n),降低后续出现问题的可能性,那么可以使用 Python 的 Pandas 库来完成,示例代码如下:

# Python 代码
import re
# 需要提前安装 pandas 库
import pandas as pd
# 你的 csv 数据的路径
filepath = "C:\FILE.csv"
data = pd.read_csv(filepath, dtype=str).fillna('')
# 对每一个单元格应用函数:将连续的换行符(至少一个)替换为四个空格
# 同时将英文逗号替换为中文逗号(如果这样做不影响你的数据质量的话)
data = data.applymap(lambda x: re.sub('\n+', '    ', x).replace(',', ','))
# 处理结束后,重新将数据进行保存/导出
data.to_csv("C:\FILE_SAVE.csv", index=False)

使用以上代码有一个前提,那就是你的计算机内存大小足以支撑 Pandas 一次性将数据读入

情形2:你的 csv 文件中不存在封闭符

出现这种情况,说明你的 csv 文件中可能夹带了分隔符(,)换行符(\n)且有可能是使用了 Stata 做的导出,且在导出时没有正确地设置封闭符选项(quoto 选项)……如果真的是这样,那么你的这份 csv 数据结构必然乱套,真的就是一份彻头彻尾的问题数据了。如果真的遇到这种情况,又可以分两种情况来讨论了,即你的数据是受异常分隔符影响还是受异常换行符影响。

(1)文件受异常分隔符影响

如果是受异常分隔符影响,那么可能导致部分行的数据值个数多于字段数量,这样的话很难判断哪些分隔符是造成数据异常的“罪魁祸首”,因此使用 Python 算法成功修正的概率并不高。

💡 当然,这并不意味着绝对无法使用 Python 算法进行修正,如果你的 csv 数据大多数字段都是表示数值或特定对象,不可能包含逗号等分隔符,即你非常确定是哪一个字段包含换行符,那么还是可以针对性地设计算法进行修正的。

对于这种不方便使用程序进行修正的数据,我们还可以使用 Python 的 Pandas 库(建议使用 Pandas 2.x 版本)进行辅助处理。原理是 Pandas 的 csv 读取函数提供了一个检测异常数据行的参数,当发现 csv 文件中存在某数据行的数据值个数大于字段数量时,就会跳过这一行并发出警告,但不会影响读取后续的行。也就是说即使数据中存在一些异常数据行,Pandas 也可以读取其他正常的数据行。示例代码如下:

# Python 代码
# 建议使用 pandas 2.0 及以上版本,这些版本的参数更加方便友好
import pandas as pd
# 你的 csv 数据的路径(可能存在异常分隔符的 csv 文件)
filepath = "C:\FILE.csv"
# 读取数据,设置参数 on_bad_lines='warn'
# 该参数有三种取值 'error', 'warn', 'skip'
# 分别代表遇错误行直接报错、警告并跳过错误行、直接跳过错误行
data = pd.read_csv(filepath, on_bad_lines='warn')
# 如果你的 pandas 版本为 1.x ,应该使用下面的代码,也能达到类似的效果
# data = pd.read_csv(filepath, error_bad_lines=False)

使用上述代码读取数据后,如果你的 csv 文件中存在异常的分隔符导致部分数据行出现错误,那么程序就会输出如下图所示的提示信息。

图片

图示:pandas 读取异常 csv 发出的提示信息

上图中程序提示,读取过程中跳过了第 3、7、16 行,原因均为预期 5 个字段,但发现了 6 个(异常分隔符所致,说明该行出现一个异常分隔符)。

如果提示信息中的异常数据行并不多,那么笔者建议你使用文本编辑器(例如 Windows 自带的记事本或第三方软件如Notepad++)直接以文本方式打开你的 csv 数据,然后人工调整一下数据,具体操作就是人工判断哪些分隔符是多余的,将其替换为其他空白符号,同时将英文逗号替换为其他符号,例如中文逗号。如果被跳过的数据行比较多,但在数据总量上占比不大,则可以考虑放弃这部分数据行。

图片

(2)文件受异常换行符影响

相对于出现异常分隔符来说,异常换行符带来的问题要更难处理一些,因为大部分工具解析 csv 数据的逻辑是,如果一行中的数据个数多于字段数量,那么就报错或者警告、跳过,但如果一行中的数据个数少于字段数量(异常换行符所致),则该行中其他缺少的字段值都会被置空,这就容易造成原本一行数据被解析为多行,并且还会造成字段错位。面对这种情况,即便是 pandas 也没有可以直接提供帮助的函数或参数。

这时我们可以根据 csv 数据的特性,编写一个 csv 数据的 Python 修复算法,只需要提供 csv 文件的路径,就可以得到修复后的表格。具体代码如下:

# Python 代码

# 需要提前安装 pandas 库
import pandas as pd  

def read_csv_with_embedded_newlines(file_path):
    # 先获取待处理 csv 数据的字段数量,逻辑是根据文件中的字段名进行判断
    expected_columns = pd.read_csv(file_path, nrows=0).shape[1]
    # 读取整个文件内容
    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()
    # 将文件内容按换行符拆分成多行
    lines = content.split('\n')
    # 初始化一个空列表用来存储修正后的行数据
    corrected_rows = []
    # 初始化一个临时列表用来存储当前正在处理的行
    current_row = []
    # 迭代处理每一行
    for line in lines:
        # 按逗号分割每一行,得到该行的所有单元格
        cells = line.split(',')
        # 如果 current_row 不为空,表示我们正在处理一个包含换行符的多行单元格
        if current_row:
            # 将当前行的第一个单元格附加到上一个单元格的末尾,并用换行符连接
            current_row[-1] += '\n' + cells[0]
            # 将当前行的剩余单元格追加到 current_row 中
            current_row.extend(cells[1:])
        else:
            # 如果 current_row 为空,直接将当前行的单元格赋值给 current_row
            current_row = cells
        # 检查当前行的列数是否符合预期
        if len(current_row) == expected_columns:
            # 如果列数符合预期,将当前行添加到修正后的行数据中
            corrected_rows.append(current_row)
            # 重置 current_row 为一个空列表,以处理下一行数据
            current_row = []
        elif len(current_row) > expected_columns:
            # 如果列数超过预期,需要将其拆分
            corrected_rows.append(current_row[:expected_columns])
            current_row = current_row[expected_columns:]
    # 如果最后一行数据是不完整的,将其添加到修正后的行数据中
    if current_row:
        corrected_rows.append(current_row)
    # 最后使用 pandas 将修复结果转为 DataFrame 表格并返回
    df = pd.DataFrame(corrected_rows[1:], columns=corrected_rows[0])
    return df

# 一个调用上述函数的示范
file_path = './Stataoutput.csv'
DF = read_csv_with_embedded_newlines(file_path)
# 函数返回的变量 DF 就是 pandas 中的表格对象 DataFrame

图片
图片

💡 注意:上述代码可能无法修复所有存在上述问题的 csv 数据。

另外,如果你的 csv 数据既受异常分隔符影响,也受异常换行符影响(也算难得)!那么修复的难度就太大了,不可控的因素有很多,笔者更建议你找到更原始的数据,然后先对原始的数据进行清洗,然后再导出,导出时尽可能添加封闭符以增加数据稳定性。例如原始数据存放在数据库中,那么可以先使用 SQL 语句将原始数据中的异常字符清理掉再导出为 csv 数据。

四、csv 文件的其他注意事项

csv 文件除了容易受特殊字符影响外,还容易受编码、工具等其他因素影响,我们曾发布过一些有关 csv 文件处理的文章,有需要或者感兴趣的朋友可以通过下面的传送门前往查看。

Python 教学 | 数据处理必备工具之 Pandas(数据的读取与导出)

Python实战 | 文本文件编码问题的 Python 解决方案

数据治理 | csv 总乱码?教你三招,习一即可!

数据治理 | 超大.csv文件怎么处理?我们有独门武器!(免费赠送自制csv切分工具)

五、结束语

💡 csv 文件结构简单,几乎所有的分析、计量工具都支持 csv 文件的导入和导出,不过也正因为如此,csv 文件的读写约束性也不够强,容易造成文件损坏。由此可见,经常用到数据的科研工作者最好对 csv 文件的结构和特点有一个基本的了解,并具备一定的文件处理能力。

六、相关推荐

Python 教学

•  Python 教学 | 学习 Python 第一步——环境安装与配置

•  Python 教学 | Python 基本数据类型

•  Python 教学 | Python 字符串操作(上)

•  Python 教学 | Python 字符串操作(下)

•  Python 教学 | Python 变量与基本运算

•  Python 教学 | 组合数据类型-列表

•  Python 教学 | 组合数据类型-集合(内含实例)

•  Python 教学 | 组合数据类型 - 字典&元组

•  Python 教学 | Python 中的分支结构(判断语句)

•  Python 教学 | Python 中的循环结构(上)

•  Python 教学 | Python 中的循环结构(下)

•  Python 教学 | Python 函数的定义与调用

•  Python 教学 | Python 内置函数

•  Python 教学 | 最常用的标准库之一 —— os

•  Python 教学 | 盘点 Python 数据处理常用标准库

•  Python 教学 | “小白”友好型正则表达式教学(一)

•  Python 教学 | “小白”友好型正则表达式教学(二)

•  Python 教学 | “小白”友好型正则表达式教学(三)

•  Python 教学 | 数据处理必备工具之 Pandas(基础篇)

•  Python 教学 | 数据处理必备工具之 Pandas(数据的读取与导出)

•  Python 教学 | Pandas 数据索引与数据选取

•  Python 教学 | Pandas 妙不可言的条件数据筛选

•  Python 教学 | Pandas 缺失值与重复值的处理方法

•  Python 教学 | Pandas 表格数据行列变换

•  Python 教学 | Pandas 表格字段类型精讲(含类型转换)

•  Python 教学 | Pandas 数据合并(含目录文件合并案例)

•  Python 教学 | Pandas 数据匹配(含实操案例)

•  Python 教学 | Pandas 函数应用(apply/map)【上】

•  Python 教学 | Pandas 函数应用(apply/map)【下】

•  Python 教学 | Pandas 分组聚合与数据排序

•  Python 教学 | Pandas 时间数据处理方法

•  Python 教学 | 列表推导式 & 字典推导式

•  Python 教学 | 一文搞懂面向对象中的“类和实例”

•  Python 教学 | Python 学习路线+经验分享,新手必看!

•  Python 教学 | 解密 Windows 中的 Path 环境变量

•  Python教学 | 有备无患!详解 Python 异常处理(try-except)

•  Python 教学 | Jupyter Notebook 中那些十分有用的魔术命令

Python实战

•  Python实战 | 如何使用 Python 调用 API

•  Python 实战 | 使用正则表达式从文本中提取指标

•  大数据分析 | 用 Python 做文本词频分析

•  数据治理 | 从“今天中午吃什么”中学习Python文本相似度计算

数据治理 | 省下一个亿!一文读懂如何用python读取并处理PDF中的表格(赠送本文所用的PDF文件)

•  数据治理 | 还在人工识别表格呢?Python 调用百度 OCR API 又快又准

•  数据治理 | 如何用 Python 批量压缩/解压缩文件

•  案例分享:使用 Python 批量处理统计年鉴数据(上)

•  案例分享:使用 Python 批量处理统计年鉴数据(下)

•  Python 实战 | ChatGPT + Python 实现全自动数据处理/可视化

•  ChatGPT在指尖跳舞: open-interpreter实现本地数据采集、处理一条龙

•  Python 实战 | 文本分析之文本关键词提取

•  Python 实战 | 文本分析工具之HanLP入门

•  Python 实战 | 进阶中文分词之 HanLP 词典分词(上)

•  Python 实战 | 进阶中文分词之 HanLP 词典分词(下)

•  Python实战 | 文本文件编码问题的 Python 解决方案

•  Python 实战 | 从 PDF 中提取(框线不全的)表格

•  Python 实战 | 利用 Python 做长宽面板转换(附数据&代码)

•  Python 实战 | 拆分、合并、转换……请查收这份 PDF 操作手册

•  答疑解惑 | 云桌面用户如何使用 Python 连接数据库读写、处理数据

•  Python 实战 | 使用 Python 清洗文本字段中的 HTML 代码

数据可视化

•  数据可视化 | 讲究!用 Python 制作词云图学问多着呢

•  数据可视化 | 地址数据可视化—教你如何绘制地理散点图和热力图

•  数据可视化 | 太酷了!用 Python 绘制3D地理分布图

•  数据可视化 | 用 Python 制作动感十足的动态柱状图

•  数据可视化 | Python绘制多维柱状图:一图展示西部各省人口变

迁【附本文数据和代码】

•  数据可视化 | 3D 柱状图一览各省农民合作社存量近十年变化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值