【问题标题】:Pass list of different named tuples to different csv by tuple name通过元组名称将不同命名元组的列表传递给不同的 csv
【发布时间】:2015-10-20 15:05:47
【问题描述】:

我有一个程序,我希望在其中记录发生的所有重大变化。例如:变量x每次变化的值记录变化的时间和变化本身。在程序中有许多这样的变化,并且并非都具有相同数量的参数。

我决定使用 namedtuples 来存储更改的每个实例,然后将这些 namedtuples 放入单个主数据列表中 - 准备导出到 csv。我使用了元组,因为它们当然是不可变的,非常适合记录保存。下面我试图以尽可能简洁的方式解释我所做的和尝试的。希望到目前为止我的问题和尝试都很清楚。

所以我有:

data = [] 

作为主存储库,namedtuples 的形式为:

a_tuple = namedtuple('x_change', ['Time', 'Change'])
another_tuple = namedtuple('y_change', ['Time', 'Change', 'id'])

然后,我可以在每次检测到数据更改时附加这些命名元组的实例,使用如下命令:

data.append(a_tuple(a_time, a_change))
data.append(another_tuple(a_time, a_change, an_id))

如果我打印出数据的内容,我会得到如下输出:

x_change(a_time=4, a_change=1)
y_change(a_time=5, a_change=3, an_id = 2)
y_change(a_time=7, a_change=1, an_id = 3)
x_change(a_time=8, a_change=3)

我想做的是通过元组名称将这些元组导出到 csv 文件。所以在上述情况下,我最终会得到两个 csv 文件,格式如下:

name, time, change
x_change, 4, 1
x_change, 8, 3

和;

name, time, change, id
y_change, 5, 3, 2
y_change, 7, 1, 3

到目前为止,我必须设法写入单个 csv,如下所示:

with open ('events.csv', 'w', newline='') as csvfile:
    output = csv.writer(csvfile, delimiter = ',')
    for row in data:
        output.writerow(row)

产生的输出减去元组名称。所以:

4, 1
5, 3, 2
7, 1, 3
8, 3

我也试过了:

with open ('events.csv', 'w', newline='') as csvfile:
    output = csv.writer(csvfile, delimiter = ',')
    for row in data:
        output.writerow(str(row))

通过获取每个字符(仅包括第一行)将文件拆分为 csv 格式,包括元组名称:

x, _, c, h, a, n, g, e, 4, 1

我已经寻找解决方案,但没有遇到任何适合我正在尝试做的事情,现在不知所措。任何帮助将不胜感激。

【问题讨论】:

    标签: python csv namedtuple


    【解决方案1】:

    以下方法应该有效。这将获取包含所有命名元组的数据,并首先按元组的类型对其进行排序。然后,它将所有条目分组并为每个条目创建一个 CSV 文件。每个 CSV 文件的第一行包含字段名称:

    from collections import namedtuple
    from itertools import groupby
    import csv
    
    data = [] 
    
    a_tuple = namedtuple('x_change', ['Time', 'Change'])
    another_tuple = namedtuple('y_change', ['Time', 'Change', 'id'])
    
    data.append(a_tuple(6, 1))
    data.append(a_tuple(2, 1))
    data.append(another_tuple(5, 3, 2))
    data.append(another_tuple(7, 1, 3))
    data.append(a_tuple(5, 2))
    
    data.sort(key=lambda x: type(x).__name__)
    
    for k, g in groupby(data, lambda x: type(x).__name__):
        with open('{}.csv'.format(k), 'w', newline='') as f_output:
            csv_output = csv.writer(f_output)
            rows = list(g)
            csv_output.writerow(['Name'] + list(rows[0]._fields))
            for row in rows:
                csv_output.writerow([type(row).__name__] + list(row))
    

    对于我的数据,这将为您提供两个 CSV 文件,如下所示:

    x_change.csv

    Name,Time,Change
    x_change,6,1
    x_change,2,1
    x_change,5,2
    

    y_change.csv

    Name,Time,Change,id
    y_change,5,3,2
    y_change,7,1,3
    

    【讨论】:

    • 完美!我现在只需要弄清楚你所做的实际上是如何工作的。谢谢。
    【解决方案2】:

    查看 namedtuple 实例表示 - __repr__

    >>>import namedtuple
    
    >>>Row = namedtuple('Row', 'time, change')
    >>>record = Row(4, 1)
    

    ...

    >>>help(record)
    class Row(builtins.tuple)
     |  Row(time, change)
     |
     |  Method resolution order:
     |      Row
     |      builtins.tuple
     |      builtins.object
     |
     |  Methods defined here:
     |
     |  __getnewargs__(self)
     |      Return self as a plain tuple.  Used by copy and pickle.
     |
     |  __getstate__(self)
     |      Exclude the OrderedDict from pickling
     |
     |  __repr__(self)
     |      Return a nicely formatted representation string
     |
     |  _asdict(self)
     |      Return a new OrderedDict which maps field names to their values.
     |
    ...
    
    
    >>> record.__repr__()
    'Row(time=4, change=1)'
    >>> repr(record)
    'Row(time=4, change=1)'
    

    从那里您可能需要进行一些解析,但这是一个很好的起点,因为存在 namedtuple 名称。

    希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      您可以通过将名称作为namedtuple 中的字段来让您的生活更轻松。

      xChange = namedtuple('xChange', ['name', 'time' 'change'])
      yChange = namedtuple('yChange', ['name', 'time', 'change', 'id'])
      

      namedtuple 没有默认值,但您可以将 xChange 子类化,例如,创建一个命名元组,在 __init__ 中将名称设置为“x_change”

      【讨论】:

        【解决方案4】:

        你需要完成两件事:

        1. 将类型的名称放入行中
        2. 为每个元组类型创建一个文件

        对于 #1,创建一个函数以返回您真正想要的行。

        def get_row_output(row):
            return [type(row).__name__] + list(row)
        

        (命名元组最终创建了一个自定义类型,其名称为namedtuple()的第一个参数的值。该名称不会使其成为该类型实例的数据的任何表示形式,因此您必须提取自己动手。)

        对于 #2,要以不同的文件结束,您需要在写出 CSV 的代码之外循环数据。最简单的方法:

        for row in data:
            with open(row.__name__ + '.csv', 'a+') as csvfile:
                output = csv.writer(csvfile, delimiter=',')
                output.writerow(get_row_output(row))
        

        (注意以追加模式打开文件,因为您在每一行上打开和关闭文件。)

        如果data 非常大,那么最好保持文件句柄打开而不是打开和关闭每一行。比如:

        def get_filename(row):
            return row.__name__ + '.csv'
        
        def write_changes(data):
            file_handles = {}
            csv_writers = {}
        
            for row in data:
                filename = get_filename(row)
                if filename not in file_handles:
                    f = open(filename, 'wb')
                    file_handles[filename] = f
                    csv_writer = csv.writer(f, delimiter=',')
                    csv_writers[filename] = csv_writer
        
                csv_writers[filename].writerow(get_row_output(row))
        
            for f in file_handles.values():
                f.close()
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-06-26
          • 2020-01-17
          • 2021-12-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多