我正在尝试构建一种优化方法,根据文件名以x:y比率拆分文件名列表(简短示例)。此文件列表是使用os.scandir获得的(更好的性能vs os.listdir,src:Python Docs scandir)。
示例 -
文件(忽略扩展名) -
A_1,A_2,... A_10(此处A为文件名,1为文件的样本编号)
B_1,B_2,... B_10
等等
假设x:y比率为7:3 所以我想在不同的列表中有70%的文件名(A_1..A7,B_1..B_7)和30%(A_8 - A_10,B_8..B_10),第一个列表应该在那里并不重要命令意味着文件可以是A_1,A_9,A_5等,只要它们在列表2中的列表1到3文件中被拆分为7个文件。
现在必须要注意的是,这个目录很大(~150k文件)并且每种类型文件的样本各不相同,也就是说文件名A的文件可能有1000个文件,也可能只有5个。 400个独特的文件名。
这个当前的解决方案根本不应该被称为解决方案,因为它违背了每个文件名的准确比率的目的。它当前正在以x:y比率将fileObjects列表(基本上像A一样,数字像1,文件A_1中的数据等)拆分,并利用条目在使用时按任意顺序生成的事实的 os.scandir
ratio_number = int(len(list_of_fileObjects) *.7)
list_70 = list_of_fileObjects[:ratio_number]
list_30 = list_of_fileObjects[ratio_number:]
我的第二种方法至少是一个有效的解决方案是为每个文件名单独创建一个列表(包括对整个文件列表进行排序),按比例拆分并为每个文件名执行此操作。我正在寻找一个更加pythonic /优雅的解决方案来解决这个问题。任何建议或帮助都将受到赞赏,特别是考虑到正在处理的数据的大小。
答案 0 :(得分:0)
如果我理解了这种情况,你试图对每个文件名前缀文件的相同比例进行分区。您当前的方法从整个文件集中选择正确的比例,但它不考虑不同的文件名前缀,因此它可能无法以正确的比例得到它们(尽管在大多数情况下它可能会稍微接近)。
您的第二种方法是首先通过前缀分隔文件名,然后对每个子列表进行分区来避免此问题。但是如果你想要一个包含所有前缀的组合列表,这种方法最终可能会浪费时间复制数据,因为你必须分开,然后按前缀重新组合单独的列表。
我认为你可以通过文件名上的单个循环来做你想要的。您需要跟踪每个文件名前缀的两个数据点:具有您为第一个样本选择的前缀的文件数以及您看过的具有该前缀的文件总数。
ratio = 0.7
prefix_dict = {} # values are lists: [number_selected_for_first_list, total_number_seen]
first_sample = [] # gets a proportion of the files equal to ratio (for each prefix)
second_sample = [] # gets the rest of the files
for filename in list_of_files:
prefix = filename.split("_", 1)[0]
selected_seen = prefix_dict.setdefault(prefix, [0, 0])
selected_seen[1] += 1
if selected_seen[0] < round(ratio * selected_seen[1]):
first_sample.append(filename)
selected_seen[0] += 1
else:
second_sample.append(filename)
此代码唯一棘手的部分是使用dict.setdefault
来获取selected_seen
列表。如果请求的prefix
在字典中尚不存在,则新值([0, 0]
)将被添加到该键下的字典中(并返回)。后面的代码修改了列表。
根据您希望如何处理不精确的比例,您可以稍微更改if
条件。我进行了round
调用(我认为它会最准确地进行分区),但代码在没有它的情况下可以正常工作(将选择偏向第二个样本)或使用selected_seen[0] <= int(ratio * selected_seen[1])
(偏向第一个样本) )。
请注意,在对每个前缀进行分区时,无论选择哪种方式进行舍入,都可能会使单独的前缀在同一方向上不平衡,从而使整个样本的失衡率超出您通常的预期。例如,如果您有10个前缀,包含10个文件(总共100个文件),则7.5的比率将导致80和20个文件的最终样本列表而不是75个和25个。这是因为每个前缀都被分区8和2(7.5回合)。如果每个文件都有唯一的前缀,那么您最终会得到第一个样本中的所有内容!如果整个样品的尺寸合适非常重要,您可能需要根据整体样品尺寸稍微调整一下样品的采样。
答案 1 :(得分:0)
我找到了解决这个问题的好方法。
all_file_names = {}
# ObjList is a list of objects but we only need
# file_name from that object for our solution
for x in ObjList:
if x.file_name not in all_file_names:
all_file_names[x.file_name] = 1
else:
all_file_names[x.file_name] += 1
trainingData = []
testData = []
temp_dict = {}
for x in ObjList:
ratio = int(0.7*all_file_names[x.file_name])+1
if x.file_name not in temp_dict:
temp_dict[x.file_name] = 1
trainingData.append(x)
else:
temp_dict[x.file_name] += 1
if(temp_dict[x.file_name] < ratio):
trainingData.append(x)
else:
testData.append(x)