pd.read_csv:不能在引号之间使用逗号分隔项目

qybjjes1  于 2022-12-06  发布在  其他
关注(0)|答案(2)|浏览(167)

我想用www.example.com _csv读取一个csv文件pd.read。
有些行还不错,但其他行被分组在第一列,其余行用nan值填充。问题是那些在标题列中有逗号的行,因此被分组在引号之间,如下所示:

Apples,Oranges,5,"These are apples, red, and oranges, orange",2;

如果您使用文本编辑器开启相同的档案,则会显示如下:

"Apples,Oranges,5,""These are apples, red, and oranges, orange"",2";

我尝试了很多不同的方法(比如delimiter =',',quotechar ='“'),但似乎都不起作用。

有什么建议吗?

mxg2im7a

mxg2im7a1#

也许你可以试着用这样的方式:

import pandas as pd

def fix_row(row: pd.Series, column_name: str) -> pd.Series:
    """Split a row into multiple rows if `column_name` is a comma separated string.

    Parameters
    ----------
    row : pd.Series
        The row to split.
    column_name : str
        The name of the column to split.

    Returns
    -------
    pd.Series
        The original row, or row created from splitting `column_name`.
    """
    value = row[column_name]
    formated_value = str(value).split(',')
    if len(formated_value) > 1:
        return pd.Series(dict(zip(row.keys(), formated_value)))
    return row

# == Example ==================================================

df = pd.DataFrame(
    {
        "col1": ["1234,2022-02-02,10", "1234", "EBX10", "EBX20,2022-02-02,10"],
        "col2": [None, "2022-02-02", "2022-03-02", None],
        "col3": [None, 10, 50, None],
    }
)
# Dataframe `df` looks like this:
#
#                   col1        col2  col3
# 0   1234,2022-02-02,10        None   NaN   <-- Column with formating problem
# 1                 1234  2022-02-02  10.0
# 2                EBX10  2022-03-02  50.0
# 3  EBX20,2022-02-02,10        None   NaN   <-- Column with formating problem

# Call `fix_row` function using apply, and specify the name of the column
# to maybe split into multiple columns.
new_df = df.apply(fix_row, column_name='col1', axis=1)

# Dataframe `new_df` looks like this:
#
#     col1        col2  col3
# 0   1234  2022-02-02    10
# 1   1234  2022-02-02  10.0
# 2  EBX10  2022-03-02  50.0
# 3  EBX20  2022-02-02    10

关于fix_row函数的说明

fix_row函数的工作基于两个假设,这些假设必须为真才能正常工作:
1.该函数假定column_name参数的值(在上面的示例中为'col1')在出现格式问题时仅包含多个逗号。
1.当有一行需要修复时,该函数假定所有行值都需要替换为您指定的column_name中的值,并且它们的顺序正确。

示例中的输入和输出

输入Pandas Dataframe df

| 列1|第2列|第3列|
| - -|- -|- -|
| 1234,2022年2月2日,10日||楠|
| 小行星一千二百三十四|2022年2月2日|10个|
| EBX 10型|2022年3月2日|五十个|
| EBX 20,2022年2月2日,10||楠|

来自df.apply(fix_row, column_name='col1', axis=1)的输出:

| 列1|第2列|第3列|
| - -|- -|- -|
| 小行星一千二百三十四|2022年2月2日|10个|
| 小行星一千二百三十四|2022年2月2日|10个|
| EBX 10型|2022年3月2日|五十个|
| EBX 20型|2022年2月2日|10个|

fix_row的变体,您可能会考虑尝试

除了检查formated_value的长度,你还可以交换if条件语句,检查该行的其他值是否为空。

def fix_row(row: pd.Series, column_name: str) -> pd.Series:
    value = row[column_name]
    formated_value = str(value).split(',')
    if row[[col for col in row.keys() if col != column_name]].isna().all():
        return pd.Series(dict(zip(row.keys(), formated_value)))
    return row
uklbhaso

uklbhaso2#

当我慢跑的时候,你的问题一直困扰着我,因为你在好的和坏的行的结尾都显示了;。CSV文件中的行通常不会以分号结尾!
我的理论是,你的文件是由一个进程创建的,这个进程写了一个分号分隔值文件,在第0列中包含了每一行逗号分隔值的内容。这个进程必须以标准的方式转义saw中的双引号,也就是在字段周围加上双引号,并用双引号替换字段中的每一个双引号。
如果这个理论是正确的,那么您可以做的就是读取该文件两次,一次是以分号分隔值的文件,然后将第0列的内容作为CSV文件读取。
下面的代码就是这样做的:

import pandas as pd
import csv
import io

buffer = io.StringIO()
with open("file.mixed-sv", newline="") as mixed_sv_file:
    reader = csv.reader(mixed_sv_file, delimiter=";")
    for row in reader:
        print(row[0], file=buffer)

buffer.seek(0)
df = pd.read_csv(buffer)
print("df:\n", df)

给定这个输入文件(我将其命名为file.mixed-sv以强调它不是纯CSV:

Fruit1,Fruit2,N,Notes,M;
Apples,Oranges,5,"These are apples, red, and oranges, orange",2;
"Apples,Oranges,5,""These are apples, red, and oranges, orange"",2";

我的脚本输出:

df:
    Fruit1   Fruit2  N                                       Notes  M
0  Apples  Oranges  5  These are apples, red, and oranges, orange   2
1  Apples  Oranges  5  These are apples, red, and oranges, orange   2

备注:

  • 我使用csv模块而不是Pandas作为初始过滤器,a)因为我更了解它,但b)因为在处理的这个阶段,你真的不需要Pandas的功能,你只需要解析文件并提取第一个字段。
  • 如果你的文件很大,这将占用大量的内存,因为我最终在内存中同时有2个副本。如果你需要更有效的内存,用磁盘上的一个实际的临时文件替换我的StringIO缓冲区。
  • 编辑:我的原始版本在内存中有三个副本,但我刚刚意识到我可以使用buffer.seek(0)将其带回开头,而不是从其内容中创建一个新的。

相关问题