Python - Нахождение, когда две строки становятся разными - PullRequest
0 голосов
/ 21 февраля 2020

Я пытаюсь извлечь названия линейки продуктов из списка названий продуктов. Это будет подмножество имени продукта и всегда будет от начала строки до произвольной точки, где имя перечисляет изменения в пределах линейки продуктов, такие как размер, цвет и т. Д. c. Это очень легко сделать человеку, но объем данных требует автоматизации этого процесса.

Ex data:

item_id        description
100            SomeBrand Medical Jackets Blue SM 
101            SomeBrand Medical Jackets Blue M
200            Acme Gloves Pink X Large
201            Acme Gloves Pink Small 100 Pack
202            Acme Gloves White X Large
203            Acme Gloves White Small

Желаемый результат:

item_id        family
100            SomeBrand Medical Jackets Blue
101            SomeBrand Medical Jackets Blue
200            Acme Gloves Pink
201            Acme Gloves Pink
202            Acme Gloves White
203            Acme Gloves White

Подход: итерация по строкам, итерация по каждой строке описания, сравнение каждый раз с описанием последнего элемента и остановка, если описание изменяется. Когда он переходит из последнего описания и содержит более 10 символов в строку, он резервирует одну позицию и называет это имя. Это также относится и к предыдущему элементу, так как он был первым в семействе и ему не с чем сравнивать, кроме несоответствующей строки. Для каждого нового описания я удостоверяюсь, что оно очень похоже на последнее с SequenceMatcher для сброса при передаче данных в новое семейство продуктов.

Я открыт для предложений о лучших способах сделать это. Мой код не работает хорошо и оставляет артефакты в конце строк. Фактические данные сильно различаются, но большинство товарных линий имеют общее начало своего описания, которое должно быть в состоянии извлечь имя семьи.

Код (Python 2.7 на Win10):

import os
import csv
from difflib import SequenceMatcher

items = []
descs = []
families = {}

with open('items_no_family.csv', 'rb') as csvFile:
    reader = csv.DictReader(csvFile)
    for row in reader:
        items.append(row['item_id'])
        descs.append(row['description'])

i = 0
last_desc = ''
for d in descs:
    p = 1
    print items[i] + ' ' + d
    if last_desc != '':
        seq = SequenceMatcher(None, last_desc, d)
        if seq.ratio() >= 0.9:
            for c in d:
                if d[0:p] != last_desc[0:p] and p > 10:
                    families[items[i]] = d[0:p-1]
                    families[items[i-1]] = d[0:p-1]
                    break
                p = p + 1
    last_desc = d
    i = i + 1

with open("items w families.csv", "ab") as f:
    for k,v in families.items():
        f.write(k + ',"' + v +'"\r\n')

Ответы [ 2 ]

0 голосов
/ 21 февраля 2020

Вы можете использовать pairwise() из itertools и использовать стек, чтобы определить, где строки меняются:

import itertools

brands = ["SomeBrand Medical Jackets Blue SM ",
          "SomeBrand Medical Jackets Blue M",
          "Acme Gloves Pink X Large",
          "Acme Gloves Pink Small 100 Pack",
          "Acme Gloves White X Large",
          "Acme Gloves White Small"]


def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = itertools.tee(iterable)
    next(b, None)
    return zip(a, b)

table = []
for pair in pairwise(brands):
    stack = ""
    for x, y in zip(pair[0], pair[1]):
        if x == y:
            stack += x
        else:
            if stack:
                trunk = stack
            break
    table.append((trunk.rstrip(), pair[0]))

print(table)

Это дает

[
    ('SomeBrand Medical Jackets Blue', 'SomeBrand Medical Jackets Blue SM '),
    ('SomeBrand Medical Jackets Blue', 'SomeBrand Medical Jackets Blue M'),
    ('Acme Gloves Pink', 'Acme Gloves Pink X Large'),
    ('Acme Gloves', 'Acme Gloves Pink Small 100 Pack'),
    ('Acme Gloves White', 'Acme Gloves White X Large')
]
0 голосов
/ 21 февраля 2020

Я объясню мою идею: вы устанавливаете пороговое значение, которое означает, сколько символов должно перекрываться хотя бы для определения нового семейства, и предполагать, что у каждого продукта есть семейство. Тем самым вы сохраняете каждый новый продукт как новую семью. Когда новый продукт пересекается с семейством для более чем порогового значения символов, вы сокращаете фамилию так, что два продукта теперь входят в это семейство

    products = [
"SomeBrand Medical Jackets Blue SM ",
"SomeBrand Medical Jackets Blue M",
"Acme Gloves Pink X Large",
"Acme Gloves Pink Small 100 Pack",
"Acme Gloves White X Large",
"Acme Gloves White Small"
]

families = list()
ifam = list() #index of the family
threshold = 15 #at least 15 characters should be equals


for prod in products:
    if len(prod) < threshold: continue
    check = False
    for j in range(len(families)):
        fam = families[j]
        if prod[:threshold] == fam[:threshold]:
            check = True #product has an already existed category
            for i in range(min(len(prod),len(fam))):

                if prod[i] != fam[i]:
                    families[j] = fam[:i] #keep only the same characters
                    ifam.append(j) #product[i] is in the j family
                    break
            ifam.append(j)    
            break
    if not check:
        families.append(prod) #this product will be used for a category

#let me see the families
for i in range(len(products)):
    print("product: \"%s\" is in the family: \"%s\"" % (products[i],families[ifam[i]]))

Если вы хотите использовать этот код помните, что вам нужно выбрать, какое пороговое значение лучше для вас, и как обращаться с продуктами, название которых короче порога

...