Python-通过常量列标题排列CSV文件的不同行

时间:2018-10-13 08:58:32

标签: python python-2.7 csv

我有一个CSV文件,该文件会按以下顺序自动更新一些数据

A,B,C,D,E,F
4,2,6,4,8,9
D,C,A,B,E,F
6,4,5,8,6,2
E,F,A,C,D
4,2,7,6,5

您会注意到,标题值在不同的行中以不同的顺序出现。有时标题列值之一也丢失。

要求是使用一致的标头及其下面的所有值对其进行排序。例如。

A,B,C,D,E,F
4,2,6,4,8,9
A,B,C,D,E,F
5,8,4,6,6,2
A,B,C,D,E,F
7, ,6,5,4,2

OR

    A,B,C,D,E,F
    4,2,6,4,8,9
    5,8,4,6,6,2
    7, ,6,5,4,2

我尝试使用以下代码对其进行排序,但是它仅对第一行进行排序,随后在其上按原样打印。

with open('mycsv.csv', 'r') as infile, open('reordered.csv', 'a') as outfile:
    fieldnames = ['A','B','C','D','E','F','G']
    writer = csv.DictWriter(outfile, fieldnames=fieldnames)
    writer.writeheader()
    for row in csv.DictReader(infile):
        writer.writerow(row)

有关如何实现此目标的任何指示都将有所帮助。谢谢。

2 个答案:

答案 0 :(得分:1)

您可以导入文件,并继续读取2行(标题+数据)并为其创建字典。您将字典添加到包含所有数据的列表中。 您将获得最大的字典(包含最多键的字典),对其进行排序,然后将所有数据写回。

在缺少键的字典中,您可以用空字符串替换其值:

创建数据文件:

with open("t.csv","w") as f:
    f.write("""A,B,C,D,E,F
4,2,6,4,8,9
D,C,A,B,E,F
6,4,5,8,6,2
E,F,A,C,D
4,2,7,6,5""")

然后:

# read in data as list of dicts, each dict contains 2 rows worth of data    
data = []
with open("t.csv") as f:
    while True:
        try:
            # get a header line and a data line
            header = next(f).strip().split(",")
            d = next(f).strip().split(",")
            # create a dict from it and append it to your data collection
            data.append( {k:v for k,v in zip(header,d)} )

        except StopIteration:
            print("done")
            break

# get a sorted set of all keys in all dicts:
keys = set()
for k in data:
    keys.update(k)
keys = sorted(keys)

# write the data again
with open("new_t.csv","w") as f:
    # write headers once
    f.write(",".join(keys))
    f.write("\n")
    for d in data:
        f.write(",".join( ( d.get(k,"") for k in keys  )))
        f.write("\n")

# check:
with open("new_t.csv","r") as f:
    print(f.read())

结果文件:

A,B,C,D,E,F
4,2,6,4,8,9
5,8,4,6,6,2
7,,6,5,4,2

我使用python3样式打印-但代码在python 2.7和3.x中工作相同。

请确保检查您的源文件中是否包含标题行和数据行,并且没有空行,否则您必须调整代码以省略空行。

答案 1 :(得分:1)

我提出了一个解决方案,其中标题可以在任何情况下发挥作用 订单。

这些值存储在字典中,我们希望有一个 到目前为止看到的(两行)

d = {}
count = 0

我们在两行上都有一个循环(这是 itertools' recipes中的grouper, 接下来,我们将逗号分隔两行,并创建一个包含 当前标题

for l1, l2 in zip(*[open(datafile)]*2):
    heads = l1.rstrip().split(',')
    vals  = l2.rstrip().split(',') 
    headset = set(heads) 

我们有一个循环来增加与每个词典相关的列表, 使用d.setdefault具有正确的初始值(请注意, [None]*0是空白列表[]),如果新的标题出现在舞台上 什么时候。例如count是3,我们将其追加到包含3个None的列表中, [None, None, None]

    for h, v in zip(heads, vals): 
        d.setdefault(h,([None]*count)).append(v) 

考虑到这对中存在的标头之后 行,我们考虑了以前看到的标题, 但不在此c.o.l。

    for h in set(d)-headset:
        d[h].append(None) 

最终我们增加计数器

    count = count+1           

现在我们准备好输出了,我们对键进行排序,我们打印键 接下来,每两行一次,我们从列表中打印 与按键关联

keys = sorted(d.keys())
print(','.join(keys))
for n in range(count):
    print(','.join(' ' if d[k][n] is None else str(d[k][n]) for k in keys))

在一起

d = {}
count = 0
for l1, l2 in zip(*[open(datafile)]*2):
    heads = l1.rstrip().split(',')
    vals  = l2.rstrip().split(',') 
    headset = set(heads) 
    for h, v in zip(heads, vals): 
        d.setdefault(h,([None]*count)).append(v) 
    for h in set(d)-headset:
        d[h].append(None) 
    count = count+1           
keys = sorted(d.keys())
print(','.join(keys))
for n in range(count):
    print(','.join(' ' if d[k][n] is None else str(d[k][n]) for k in keys))

测试

$ cat dat.csv 
A,B,C,D,E,F
4,2,6,4,8,9
D,C,A,B,E,F
6,4,5,8,6,2
E,F,A,C,D
4,2,7,6,5
$ cat head.py 
d = {}
count = 0

for l1, l2 in zip(*[open('dat.csv')]*2):
    heads = l1.rstrip().split(',')
    vals  = l2.rstrip().split(',') 
    headset = set(heads) 
    for h, v in zip(heads, vals): 
        d.setdefault(h,([None]*count)).append(v) 
    for h in set(d)-headset:
        d[h].append(None) 
    count = count+1

keys = sorted(d.keys())
print(','.join(keys))
for n in range(count):
    print(','.join(' ' if d[k][n] is None else str(d[k][n]) for k in keys))
$ python head.py 
A,B,C,D,E,F
4,2,6,4,8,9
5,8,4,6,6,2
7, ,6,5,4,2
$