Python 如何将值列表写入一个";单元格“;在csv文件中?

Python 如何将值列表写入一个";单元格“;在csv文件中?,python,pandas,csv,Python,Pandas,Csv,我有一个循环,为每次迭代生成一个值列表。我希望将该值列表保存为csv“单元格”中的单个字符串。具体而言,我所拥有的是: index,username,user_id,data 1,name1,1,[string1,string2,string3,string4,...] 2,name2,2,[string5,string6,string7,string8,...] ... 资料 get_data返回字符串列表。与以逗号分隔的方式将这些字符串写入csv文件不同,我希望以嵌套方式将它们写入csv文

我有一个循环,为每次迭代生成一个值列表。我希望将该值列表保存为csv“单元格”中的单个字符串。具体而言,我所拥有的是:

index,username,user_id,data
1,name1,1,[string1,string2,string3,string4,...]
2,name2,2,[string5,string6,string7,string8,...]
...
资料

get_data
返回字符串列表。与以逗号分隔的方式将这些字符串写入csv文件不同,我希望以嵌套方式将它们写入csv文件。如下所示:

index,username,user_id,data
1,name1,1,[string1,string2,string3,string4,...]
2,name2,2,[string5,string6,string7,string8,...]
...
更新:


我希望将结果存储在此表单中的原因之一是,我计划为每个用户保存更多数据。此数据的长度可能与my
get_data
调用的输出长度不同。

解决问题的两个关键概念:

  • 熊猫
    .to_csv()
    在自动报价方面非常聪明。即使数据包含文字引号或逗号,在不更改默认分隔符的情况下读取/写入csv文件也不会有问题

  • 读取这种格式的问题:最初是列表的数据单元在csv文件中存储为字符串,因此它们作为字符串加载
    ast.literal\u eval()
    可以将列表的字符串表示形式转换回列表警告:此度量可能容易受到脏数据的攻击。确保在保存csv文件之前执行数据清理。

  • 实验代码

    import pandas as pd
    import ast  # for literal string parsing
    
    # data
    df = pd.DataFrame(
        data={
            "index": range(1,5),
            "username": [f"name{i}" for i in range(4)],
            "user_id": range(1,5),
            # initialize each data cell with an empty list
            "data": [list() for _ in range(4)]
        }
    )
    
    # use `.at[]` to append some values
    df.at[0, "data"] += [1, 2, 3]
    df.at[2, "data"] += [4]
    # mixed types, quotes and commas
    df.at[3, "data"] += [1, '"', -3.3, "'", ",,,", ";"]
    
    print(df)
    
    # save
    file_path = "/mnt/ramdisk/out.csv"
    df.to_csv(file_path)
    
    # load
    df_read = pd.read_csv(file_path, index_col=0)
    # parse, because contents in data cells were loaded as strings
    for i in range(len(df_read)):
        s = df_read.iat[i, 3]
        print(f"row {i} before: {type(s)}")
        df_read.iat[i, 3] = ast.literal_eval(s)
        print(f"       after: {type(s)}, len={len(s)}")
    
    print(df_read)  # identical to df
    
    # check elements within list
    ls = df_read.iat[3,3]
    for i in range(len(ls)):
        print(f"row {i}: {type(ls[i])}, contents={ls[i]}")
    
    结果

    (0)原始数据帧(未打印引用,但不影响数据本身)

    (1) 原始csv文件

    bill@bill-laptop-deb: /mnt/ramdisk
    $ cat out.csv                                                                                                                                                    
    ,index,username,user_id,data
    0,1,name0,1,"[1, 2, 3]"
    1,2,name1,2,[]
    2,3,name2,3,[4]
    3,4,name3,4,"[1, '""', -3.3, ""'"", ',,,', ';']"
    
    (2) 用python重新加载文件

    df_read  # identical to input
    Out[8]: 
    index username  user_id                     data
    0      1    name0        1                [1, 2, 3]
    1      2    name1        2                       []
    2      3    name2        3                      [4]
    3      4    name3        4  [1, ", -3.3, ', ,,,, ;]
    
    (3) LibreOffice 6也正确导入csv文件

    (4) 对加载的数据进行类型检查

    ls = df_read.iat[3,3]  # a mixed-up list
    for i in range(len(ls)):
        print(f"row {i}: {type(ls[i])}, contents={ls[i]}")
    
    row 0: <class 'int'>, contents=1
    row 1: <class 'str'>, contents="
    row 2: <class 'float'>, contents=-3.3
    row 3: <class 'str'>, contents='
    row 4: <class 'str'>, contents=,,,
    row 5: <class 'str'>, contents=;
    
    ls=df_read.iat[3,3]#一个混合列表
    对于范围内的i(len(ls)):
    打印(f“行{i}:{type(ls[i])},内容={ls[i]}”)
    第0行:,内容=1
    第1行:,内容=“
    第2行:,内容=-3.3
    第3行:,内容=
    第4行:,内容=,,,
    第5行:,内容=;
    
    可以看出,尽管引用/转义使文件很难被人阅读,但对熊猫和图书馆办公室来说,这应该不是问题


    旁注:出于可追溯性和数据完整性的考虑,强烈建议使用数据库。这种连续的数据扩充场景正是数据库系统设计的目的。如果您的项目独立运行,SQLite应该是一个轻量级的选择,在部署方面相对容易。

    这一问题的两个关键概念ms:

  • Pandas
    .to_csv()
    在自动引用方面非常聪明。在不更改默认分隔符的情况下读取/写入csv文件应该没有问题,即使数据包含文字引号或逗号

  • 读取这种格式时出现问题:最初是列表的数据单元在csv文件中存储为字符串,因此它们被加载为字符串。
    ast.literal_eval()
    可以将列表的字符串表示形式转换回列表。警告:此度量可能容易受到脏数据的攻击。请确保在保存csv文件之前执行数据清理。

  • 实验代码

    import pandas as pd
    import ast  # for literal string parsing
    
    # data
    df = pd.DataFrame(
        data={
            "index": range(1,5),
            "username": [f"name{i}" for i in range(4)],
            "user_id": range(1,5),
            # initialize each data cell with an empty list
            "data": [list() for _ in range(4)]
        }
    )
    
    # use `.at[]` to append some values
    df.at[0, "data"] += [1, 2, 3]
    df.at[2, "data"] += [4]
    # mixed types, quotes and commas
    df.at[3, "data"] += [1, '"', -3.3, "'", ",,,", ";"]
    
    print(df)
    
    # save
    file_path = "/mnt/ramdisk/out.csv"
    df.to_csv(file_path)
    
    # load
    df_read = pd.read_csv(file_path, index_col=0)
    # parse, because contents in data cells were loaded as strings
    for i in range(len(df_read)):
        s = df_read.iat[i, 3]
        print(f"row {i} before: {type(s)}")
        df_read.iat[i, 3] = ast.literal_eval(s)
        print(f"       after: {type(s)}, len={len(s)}")
    
    print(df_read)  # identical to df
    
    # check elements within list
    ls = df_read.iat[3,3]
    for i in range(len(ls)):
        print(f"row {i}: {type(ls[i])}, contents={ls[i]}")
    
    结果

    (0)原始数据帧(未打印引用,但不影响数据本身)

    (1) 原始csv文件

    bill@bill-laptop-deb: /mnt/ramdisk
    $ cat out.csv                                                                                                                                                    
    ,index,username,user_id,data
    0,1,name0,1,"[1, 2, 3]"
    1,2,name1,2,[]
    2,3,name2,3,[4]
    3,4,name3,4,"[1, '""', -3.3, ""'"", ',,,', ';']"
    
    (2) 用python重新加载文件

    df_read  # identical to input
    Out[8]: 
    index username  user_id                     data
    0      1    name0        1                [1, 2, 3]
    1      2    name1        2                       []
    2      3    name2        3                      [4]
    3      4    name3        4  [1, ", -3.3, ', ,,,, ;]
    
    (3) LibreOffice 6也正确导入csv文件

    (4) 对加载的数据进行类型检查

    ls = df_read.iat[3,3]  # a mixed-up list
    for i in range(len(ls)):
        print(f"row {i}: {type(ls[i])}, contents={ls[i]}")
    
    row 0: <class 'int'>, contents=1
    row 1: <class 'str'>, contents="
    row 2: <class 'float'>, contents=-3.3
    row 3: <class 'str'>, contents='
    row 4: <class 'str'>, contents=,,,
    row 5: <class 'str'>, contents=;
    
    ls=df_read.iat[3,3]#一个混合列表
    对于范围内的i(len(ls)):
    打印(f“行{i}:{type(ls[i])},内容={ls[i]}”)
    第0行:,内容=1
    第1行:,内容=“
    第2行:,内容=-3.3
    第3行:,内容=
    第4行:,内容=,,,
    第5行:,内容=;
    
    可以看出,尽管引用/转义使文件很难被人阅读,但对熊猫和图书馆办公室来说,这应该不是问题


    旁注:为了可追溯性和数据完整性,强烈建议使用数据库。这种连续的数据扩充场景正是数据库系统设计的目的。如果您的项目独立运行,SQLite应该是一个轻量级的选择,在部署方面相对容易。

    IMHO,这将是一个非常糟糕的存储
    csv
    的方法,因为它破坏了您的csv结构。我必须承认,我也不是特别喜欢它。但是,如果不使用不同的文件类型或数据库,您将如何存储它呢?每个用户有多行,每个行对应
    get_data
    响应中的一个字符串。或者只对值使用不同的分隔符,即
    为每个用户使用多行将使添加不同长度的数据变得困难或混乱。代码中包含了什么内容;分隔符看起来像?嗯,这将是一个非常糟糕的方式来存储
    csv
    ,因为它破坏了您的csv结构。我必须承认,我也不是特别喜欢它。但是,如果不使用不同的文件类型或数据库,您将如何存储它呢?每个用户有多行,每个行对应
    get_data
    响应中的一个字符串。或者只对值使用不同的分隔符,即
    为每个用户使用多行将使添加不同长度的数据变得困难或混乱。代码中包含了什么内容;分隔符看起来像什么?再次警告:不要为了生产目的而这样做。这并没有针对所有类型的脏数据进行广泛的测试。只需将它们存储在数据库中。再次警告:不要出于生产目的而这样做。这并没有针对所有类型的脏数据进行广泛的测试。只需将它们存储在数据库中。