如何根据匹配的子字符串从一个列表创建多个列表?

时间:2019-01-24 00:06:37

标签: python python-3.x list substring

我在python中有一个由各种文件名组成的字符串列表,像这样(但更长):

all_templates = ['fitting_file_expdisk_cutout-IMG-HSC-I-18115-6,3-OBJ-NEP175857.9+655841.2.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-3,3-OBJ-NEP180508.6+655617.3.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-1,8-OBJ-NEP180840.8+665226.2.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-6,7-OBJ-NEP175927.6+664230.2.feedme', 'fitting_file_expdisk_cutout-IMG-HSC-I-18114-0,5-OBJ-zsel56238.feedme', 'fitting_file_devauc_cutout-IMG-HSC-I-18114-0,3-OBJ-NEP175616.1+660601.5.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-6,4-OBJ-zsel56238.feedme']

我想为具有相同对象名称的元素创建多个较小的列表(子字符串以OBJ-开始,紧接在.feedme之前)。所以我会有一个这样的列表:

obj1 = ['fitting_file_expdisk_cutout-IMG-HSC-I-18114-0,5-OBJ-zsel56238.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-6,4-OBJ-zsel56238.feedme']

,对于其他匹配的“对象”,依此类推。实际上,我有900多个唯一的“对象”,并且原始列表all_templates具有4000多个元素,因为每个对象都有3个或更多单独的模板文件(它们以随机顺序显示)。因此,最终我将要拥有900多个列表(每个对象一个)。我该怎么办?

编辑:这是我尝试过的方法,但是它为我提供了每个子列表内的所有原始模板文件名的列表(每个文件名对于一个对象名称都是唯一的)。

import re
# Break up list into multiple lists according to substring (object name)
obj_list = [re.search(r'.*(OBJ.+)\.feedme', filename)[1] for filename in all_template_files]
obj_list = list(set(obj_list)) # create list of unique objects (remove duplicates)

templates_objs_sorted = [[]]*len(obj_list)
for i in range(len(obj_list)):
    for template in all_template_files:
        if obj_list[i] in template:
            templates_objs_sorted[i].append(template)

3 个答案:

答案 0 :(得分:1)

from collections import defaultdict
from pprint import pprint

all_templates = ['fitting_file_expdisk_cutout-IMG-HSC-I-18115-6,3-OBJ-NEP175857.9+655841.2.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-3,3-OBJ-NEP180508.6+655617.3.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-1,8-OBJ-NEP180840.8+665226.2.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-6,7-OBJ-NEP175927.6+664230.2.feedme', 'fitting_file_expdisk_cutout-IMG-HSC-I-18114-0,5-OBJ-zsel56238.feedme', 'fitting_file_devauc_cutout-IMG-HSC-I-18114-0,3-OBJ-NEP175616.1+660601.5.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-6,4-OBJ-zsel56238.feedme']

# simple helper function to extract the common object name
# you could probably use Regex... but then you'd have 2 problems
def objectName(path):
    start = path.index('-OBJ-')
    stop = path.index('.feedme')
    return path[(start + 5):stop]

# I really wanted to use a one line reduce here, but... 
grouped = defaultdict(list)
for each in all_templates:
    grouped[objectName(each)].append(each)
pprint(grouped)

ASIDE / TANGENT

好的,确实让我感到困惑,因为我无法在那里使用reduce做一个简单的班轮。最终,我希望python具有良好的groupby函数。它具有该名称的功能,但仅限于连续键。 Smalltalk,Objc和Swift都具有groupby机制,这些机制基本上使您可以通过任意传递函数来存储可说话的东西。

我最初的尝试是:

grouped = reduce(
    lambda accum, each: accum[objectName(each)].append(each),
    all_templates,
    defaultdict(list))

问题是lambda。 Lambda限于单个表达式。为了使其在reduce中起作用,它最多返回累积参数的修改版本。但是python除非有必要,否则不喜欢从函数/方法中返回内容。即使我们将append替换为<accessTheCurrentList> + [each],我们也需要一种字典修改方法,该方法可以更新键上的值并返回修改后的字典。我找不到这样的东西。

但是,我们可以做的是将更多信息加载到累加器中,例如元组。我们可以使用元组的一个插槽来保持传递defaultdict指针,而另一个插槽可以捕捉到对修改操作无益的None返回。它最终很难看,但它是一个衬里:

from functools import reduce
grouped = reduce(
    lambda accum, each: (accum[0], accum[0][objectName(each)].append(each)),
    all_templates,
    (defaultdict(list), None))[0]

答案 1 :(得分:0)

您可以对排序列表进行分组:

from itertools import groupby
import re

all_templates = ['fitting_file_expdisk_cutout-IMG-HSC-I-18115-6,3-OBJ-NEP175857.9+655841.2.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-3,3-OBJ-NEP180508.6+655617.3.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-1,8-OBJ-NEP180840.8+665226.2.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-6,7-OBJ-NEP175927.6+664230.2.feedme', 'fitting_file_expdisk_cutout-IMG-HSC-I-18114-0,5-OBJ-zsel56238.feedme', 'fitting_file_devauc_cutout-IMG-HSC-I-18114-0,3-OBJ-NEP175616.1+660601.5.feedme', 'fitting_file_sersic_cutout-IMG-HSC-I-18115-6,4-OBJ-zsel56238.feedme']

pattern = re.compile(r'OBJ-.*?\.feedme$')
objs = {name: pattern.search(name)[0] for name in all_templates}
result = [list(g) for k, g in groupby(sorted(all_templates, key=objs.get), key=objs.get)]

print(result)

输出:

[['fitting_file_devauc_cutout-IMG-HSC-I-18114-0,3-OBJ-NEP175616.1+660601.5.feedme'],
 ['fitting_file_expdisk_cutout-IMG-HSC-I-18115-6,3-OBJ-NEP175857.9+655841.2.feedme'],
 ['fitting_file_sersic_cutout-IMG-HSC-I-18115-6,7-OBJ-NEP175927.6+664230.2.feedme'],
 ['fitting_file_sersic_cutout-IMG-HSC-I-18115-3,3-OBJ-NEP180508.6+655617.3.feedme'],
 ['fitting_file_sersic_cutout-IMG-HSC-I-18115-1,8-OBJ-NEP180840.8+665226.2.feedme'],
 ['fitting_file_expdisk_cutout-IMG-HSC-I-18114-0,5-OBJ-zsel56238.feedme',
  'fitting_file_sersic_cutout-IMG-HSC-I-18115-6,4-OBJ-zsel56238.feedme']]

答案 2 :(得分:0)

使用regular expression方法,因此需要

import re

鉴于文件名列表,我对其进行了自定义以显示结果:

all_templates = ['aaa-OBJ-NEP175857.9+655841.2.feedme',
                 'bbb-OBJ-NEP175857.9+655841.2.feedme',
                 'ccc-OBJ-NEP175857.9+655841.2.feedme',
                 'ddd-OBJ-whathever.feedme',
                 'eee-OBJ-whathever.feedme',
                 'fff-SUBJ-whathever.feedme',
                 'fff-OBJ.feedme'
                ]

这可以是一个选择:

result = {}
for filename in all_templates:
  match = re.search('OBJ-(.+?).feedme', filename)
  if match:
    result.setdefault(match.group(1), list()).append(filename)
  else:
    result.setdefault('no-match', list()).append(filename)

它使用OBJ-.feedme之间的子字符串作为字典的键,并附加具有相同子字符串的每个文件名。如果没有匹配项,它将使用“ no-match”来附加不匹配搜索的子字符串。

因此,它返回:

print(result)
# {'NEP175857.9+655841.2': ['aaa-OBJ-NEP175857.9+655841.2.feedme', 'bbb-OBJ-NEP175857.9+655841.2.feedme', 'ccc-OBJ-NEP175857.9+655841.2.feedme'],
#  'whathever': ['ddd-OBJ-whathever.feedme', 'eee-OBJ-whathever.feedme'],
#  'no-match': ['fff-SUBJ-whathever.feedme', 'fff-OBJ.feedme']}

如果仅需要组列表:

list(result.values())