【问题标题】:Output comes in dictionary sorted order in python输出在python中以字典排序顺序出现
【发布时间】:2015-09-04 09:19:57
【问题描述】:

我有一个 csv 文件集合,其名称类似于 2.csv3.csv ....、999.csv。每个文件有 91 行。我想要一组新文件从所有文件中收集特定行。例如。 row1.csv 应该有所有 998 个文件的第一行,同样 row35.csv 应该有所有 998 个文件的第 35 行。因此,在我的脚本完成运行后,我总共应该有 91 个文件(每行一个),每个文件有 998 行(每个原始文件一个)。

我使用以下代码来完成任务

import glob
import os
for i in range(2,92):
  outfile = open("row_%i.csv" %i,'w')
  for filename in glob.glob('DataSet-MediaEval/devFeatures/*.csv'):
    with open(filename, 'r') as infile:
      lineno = 0 
      for line in infile:
        lineno += 1
        if lineno == i:
          outfile.write(line)
  outfile.close()

现在在任何 outfile row_i.csv 中,我的数据都按字典排序顺序排列。示例:

row_50.csv 文件中的第一行是10.csv 的第 50 行。

换句话说,在任何row_i.csv 中,行来自10.csv100.csv、101.csv 等等。

我想知道为什么会发生这种情况,有没有一种方法可以确保我的 row_i.csv 以文件的顺序排列,即2.csv3.csv 等等。

感谢您花时间阅读本文。

【问题讨论】:

  • 可能glob 正在对文件名进行排序,这些文件名是字符串。只需将文件名缓存在列表中并自定义排序该列表。嗯,刚刚globed 了一些随机文件,它们以随机顺序出现。无论如何,您必须对文件名进行排序。
  • @PadraicCunningham 对于我希望添加的每个行号,我将遍历所有文件一次。看到最外面的 for 循环。行数从 2 -- 92
  • @tobias_k 我不会一次存储所有文件。我打开文件提取行并将其关闭。所以你的意思是我应该以某种方式将文件名存储在 outfile 中,然后根据文件名单独对每个 outfile 进行排序。
  • 听起来你的意思是“目录”,而不是“字典”,顺序。对吗?

标签: python csv file-io export-to-csv


【解决方案1】:

不确定这是否可行或是否还有更多问题,但似乎glob 以排序顺序(按字符串排序)或随机顺序返回文件名。在这两种情况下,您都必须从文件名中提取数字并按该数字排序。

试试这样的:

p = re.compile(r"/(\d+)\.csv")
filenames = glob.glob(...)
for filename in sorted(filenames, key=lambda s: int(re.search(p, s).group(1))):
    ...

此外,您似乎一次又一次地打开、循环和关闭所有 92 个输出文件的所有 999 个文件!最好一次打开所有 92 个输出文件并将它们存储在字典中,将行号映射到文件。这样,您只需循环 999 个文件一次。

类似这样的东西(完全没有测试):

outfiles = {i: open("row_%i.csv" %i, 'w') for i in range(2,92)}
p = re.compile(r"/(\d+)\.csv")
filenames =  glob.glob('DataSet-MediaEval/devFeatures/*.csv'):
for filename in sorted(filenames, key=lambda s: int(re.search(p, s).group(1))):
    with open(filename, 'r') as infile:
        for lineno, line in enumerate(infile):
            outfiles[lineno].write(line)
for outfile in outfiles.values():
    outfile.close()

【讨论】:

  • 一次写入所有文件肯定是一种更高效且可能更快的方法——尽管您可能希望使用try/finally 来确保它们都始终关闭。
【解决方案2】:

您需要在开始迭代之前对文件名列表进行排序。这可以帮助您:

import re
import glob

filename_list = glob.glob('DataSet-MediaEval/devFeatures/*.csv')

def splitByNumbers(x):
    r = re.compile('(\d+)')
    l = r.split(x)
    return [int(y) if y.isdigit() else y for y in l]

filenames = sorted(filename_list, key = splitByNumbers)

那么你可以使用代替

for filename in glob.glob('DataSet-MediaEval/devFeatures/*.csv'):

这个

for filename in filenames:

【讨论】:

  • 这将按字典顺序对文件名进行排序,而不是按数字排序,因此例如结果顺序仍然是10.csv100.csv101.csv2.csv 等。
  • 不是真的。在python2.7上测试。函数 splitByNumbers 正好用于数字排序。试试看,你会自己看到的。
  • 嗯,它似乎确实有效,但是按所有段而不是仅按数字排序可能有点开销。再说一次,这种方法可能更通用,即如果每个文件除了数字之外的其余部分不同,它也可以工作。竖起大拇指。不过,可能会将re.compile 放在函数之外。
  • 排序问题是该函数将每个文件名转换为单个数字的列表并使用它们作为排序键。结果与按数字排序不同。即sorted([[1], [2], [1,0,0]]) 按此顺序生成:[[1], [1, 0, 0], [2]]
  • @martineau 也许这取决于 Python 版本。在 Python 2.7 和 3.4 中,它会将 "foo34bar12" 拆分为 ['foo', '34', 'bar', '12', ''],这样可以很好地排序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-27
  • 1970-01-01
  • 2016-08-01
  • 1970-01-01
  • 2022-08-12
  • 2014-05-27
  • 1970-01-01
相关资源
最近更新 更多