【问题标题】:Finding random pairs in a large directory在大目录中查找随机对
【发布时间】:2016-08-23 20:19:08
【问题描述】:

我有大约 500 万个 csv 文件存储在大约 100.000 个文件夹中。每个文件夹包含大致相同数量的文件,并且文件夹中的文件数量总是偶数。对于统计建模项目,我需要找到所有这些文件的路径并将它们以有点奇怪的顺序加载到列表中。

我特别需要坚持以下几点:

  • 唯一性:每个文件只能在列表中出现一次
  • 对:每个文件必须与同一文件夹中的另一个文件相邻(如果由于随机性,它可以相邻两个)
  • 随机性:任何两个未“配对”的文件彼此相邻的概率应该相同(即,不能仅对所有文件进行迭代)

我在下面创建了一个示例。

文件

Folder_1
- File_A
- File_B
- File_C
- File_D
Folder_2
- File_E
- File_F
- File_G
- File_H

良好的结果(随机,但遵守配对规则)

paths = ['Folder_1/File_A', 'Folder_1/File_D', 'Folder_2/File_G',  'Folder_2/File_F',  'Folder_2/File_E', 'Folder_2/File_H', 'Folder_1/File_C', 'Folder_1/File_B']

一个简单的方法可能是“选择一个随机文件夹,在该文件夹中选择一个随机文件,然后在文件夹中选择一个随机对。将这些选择保存在列表中以避免再次被选择。重复。”。但是,这将花费太长时间。你能推荐一个创建这个列表的好策略吗?如果需要,可以稍微放宽随机性要求。

【问题讨论】:

  • 你尝试过什么吗?
  • 是的,我已经完成了我在底部描述的简单方法的粗略版本。
  • 文件/文件夹名称是否遵循模式?
  • 你需要把它列成一个列表吗?或者可能是,例如发电机?
  • 生成器也可以工作。命名没有规律。

标签: python performance io


【解决方案1】:

确保一切都是随机的一种方法是使用random.shuffle,它会就地随机播放列表。这样,您可以简单地将每个项目与其邻居配对,并且知道配对是随机的。为了获得像您的示例这样的结果,您可以将结果对列表打乱并展平。这是一个例子:

from random import shuffle

# generate some sample directory names
ls = [[]] * 5
i = 0
while i < len(ls):
    ls[i] = [str(i) + chr(j) for j in range(97,101)]
    i += 1

# shuffle files within each directory
pairs = []
for l in ls:
    shuffle(l)
    pairs += list(zip(l[1::2], l[::2]))

# shuffle and flatten the list of pairs
shuffle(pairs)
flat = [item for sublist in pairs for item in sublist]    
print(flat)

【讨论】:

  • 谢谢 - 这行得通!我有一个非常相关的任务,我需要在其中找到这样的列表,但是每对必须包含不同文件夹中的文件。其他要求仍然适用。您对如何修改此相关任务的解决方案有什么建议吗?
  • 当然!我只是将原始列表展平,然后将其洗牌。由于您有 100,000 个文件夹,因此来自同一文件夹的每对两个文件的可能性很小,因此尽管不能保证它应该可以正常工作。
  • 是的,我也在考虑。也许我应该忽略那个小的错误机会:)谢谢。
【解决方案2】:

最好的策略是在线程的帮助下分而治之。您希望尽快将文件名加载到内存中以最快完成。

第一步是为文件夹名称创建一个队列,为每个文件夹中的文件列表创建另一个队列。比如:

folders = queue.Queue()
files = queue.Queue()

队列很像一个列表,只是它可以在不同的线程之间安全地共享。使用线程一次处理多个文件夹将加快处理时间。

创建一个函数来获取每个文件夹的路径,然后存储每个路径“文件夹”队列。

folderPaths = getFolderPaths()
for path in folderPaths:
    folders.put(path)

最终你会得到一个包含所有文件夹路径的队列(例如 ["Folder A", "Folder B", ...])

为线程创建一个工作函数,该函数将遍历文件夹中的所有文件并将每个文件的名称存储在列表中。总体思路是:

def threadJob():
    while True:
        folderPath = folders.get()
        if folderPath is None:
           break
        fileNames = getFilesInFolder(folderPath)
        files.put(fileNames)

这里的“getFilesInFolder()”是一个函数,它接受一个路径参数并返回该文件夹中所有文件的列表。

当所有线程完成时,“文件”应该是每个文件夹的文件列表。现在您需要将队列清空到常规列表中。

fileList = []
file = files.get()
while file not None:
    fileList.append(file)
    file = files.get()

现在您有一个列表列表。打乱每个列表以随机化文件顺序:

for files in fileList:
    random.shuffle(files)

现在您可以创建最终列表 (statList) 并从随机选择的子列表中弹出成对的文件,直到所有文件都被附加:

statList = []

while len(finalFileList > 0):
    index = random.randrange(len(fileList))
    if len(fileList[index]) == 0:
        fileList.pop(index)
    else:
        statList.append(fileList.pop())
        statList.append(fileList.pop())

不保证这会很快,但这是我能想到的最快的方法。

如果有兴趣,可以了解有关队列和线程的更多信息:

https://docs.python.org/3/library/queue.html

http://www.tutorialspoint.com/python3/python_multithreading.htm

【讨论】:

    猜你喜欢
    • 2014-01-13
    • 1970-01-01
    • 1970-01-01
    • 2011-12-02
    • 2016-05-31
    • 2018-08-01
    • 2020-04-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多