У меня есть следующие данные (представлены в списке в моем коде):

word_list = [{'bottom': Decimal('58.650'),  
  'text': 'Contact'
 },
 {'bottom': Decimal('77.280'),  
  'text': 'email@domain.com'
 },
 {'bottom': Decimal('101.833'),
  'text': 'www.domain.com'
 },
 {'bottom': Decimal('116.233'),
  'text': '(Acme INC)'
 },
 {'bottom': Decimal('74.101'),
  'text': 'Oliver'
 },
 {'bottom': Decimal('90.662'),
  'text': 'CEO'
 }]

Приведенные выше данные получены из PDF-текста. Я пытаюсь разобрать это и сохранить форматирование макета, основываясь на значениях bottom.

Смысл в том, чтобы проверить значение bottom для текущего слова, а затем найти все подходящие слова, то есть в определенном диапазоне с допуском {{ X1 } } .

Это мой код:

threshold = float('10')
current_row = [word_list[0], ]
row_list = [current_row, ]

for word in word_list[1:]:

    if abs(current_row[-1]['bottom'] - word['bottom']) <= threshold:
       # distance is small, use same row
       current_row.append(word)
    else:
       # distance is big, create new row
       current_row = [word, ]
       row_list.append(current_row)

Таким образом, это вернет список слов в пределах утвержденного порога.

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

Например, если слово имеет нижнее значение, близкое к слову, которое уже добавлено в row_list, оно просто снова добавит его в список.

Мне было интересно, возможно ли было удалить слова, которые уже были повторены / добавлены? Что-то типа:


if abs(current_row[-1]['bottom'] - word['bottom']) <= threshold:
   [...]
else:
   [...]

del word from word_list

Однако я не уверен, как это реализовать? Поскольку я не могу изменить word_list внутри цикла.

2
oliverbj 28 Июн 2019 в 17:51

3 ответа

Лучший ответ

Вы можете указать параметр сортировки, например,

word_list.sort(key=lambda x: x['bottom'])

Это приводит к

word_list.sort(key=lambda x: x['bottom'])
rows = []
current = [word_list.pop(0)]  # reversing the sort and using pop() is more efficient
while word_list:
    if word_list[0]['bottom'] - current[-1]['bottom'] < threshold:
        current.append(word_list.pop(0))
    else:
        rows.append(current)
        current = [word_list.pop(0)]
rows.append(current)

Код перебирает word_list до тех пор, пока он не станет пустым. Текущее слово (в позиции 0, хотя обратное направление увеличит эффективность) сравнивается с последним упорядоченным словом. Конечный результат (pprint.pprint(rows)):

[[{'bottom': Decimal('58.650'), 'text': 'Contact'}],
 [{'bottom': Decimal('74.101'), 'text': 'Oliver'},
  {'bottom': Decimal('77.280'), 'text': 'email@domain.com'}],
 [{'bottom': Decimal('90.662'), 'text': 'CEO'}],
 [{'bottom': Decimal('101.833'), 'text': 'www.domain.com'}],
 [{'bottom': Decimal('116.233'), 'text': '(Acme INC)'}]]
1
serv-inc 29 Июн 2019 в 04:08

Вы можете использовать цикл while вместо цикла for

while len(word_list[1:])!=0:
    word=word_list[1] #as you are deleting item once it is used, next item will come to the beginning of list automatically
    word_list.remove(word)
    if abs(current_row[-1]['bottom'] - word['bottom']) <= threshold:
       [...]
    else:
       [...]
1
amal jith 29 Июн 2019 в 03:49
bottoms = []
for w in word_list:
    bottoms.append(w["bottom"])

current_row = []
row_list = []
key = sorted(bottoms)[0]
threshold = float("10")
for b in sorted(bottoms):
    if abs(b-key) <= threshold:
        idx = bottoms.index(b)
        current_row.append(word_list[idx])
    else:
        row_list.append(current_row)
        idx = bottoms.index(b)
        current_row = [word_list[idx]]
        key = b

for row in row_list:
    print(row)

Это всегда пороги по сравнению с наименьшим значением, начинающимся с новой строки, и вывод

[{'bottom': Decimal('58.650'), 'text': 'Contact'}]
[{'bottom': Decimal('74.101'), 'text': 'Oliver'}, {'bottom': Decimal('77.280'), 'text': 'email@domain.com'}]
[{'bottom': Decimal('90.662'), 'text': 'CEO'}]
[{'bottom': Decimal('101.833'), 'text': 'www.domain.com'}]
0
Robert Guggenberger 28 Июн 2019 в 18:01