Advertisement
Korotkodul

S_Lord of The Rings

Oct 28th, 2021 (edited)
101
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.09 KB | None | 0 0
  1. """
  2. В этом номере я восопльзовался результатами номеров 2-3, например, использую найденные индексы начала
  3. и конца каждой главы в тексте книги "Братство кольца"
  4. """
  5.  
  6. from pdfminer.high_level import extract_text
  7. import json
  8. import requests
  9.  
  10. file = "j-r-r-tolkien-lord-of-the-rings-01-the-fellowship-of-the-ring-retail-pdf.pdf"
  11. text = extract_text(file)
  12. txt_list = text.split() #список всех слов книги, разделённых пробелом
  13.  
  14. #Так пишутся названия глав в скачанном тексте:
  15. chapters_titles_in_pfd = [
  16.     "A LONG-EXPECTED PARTY",
  17.     "THE SHADOW OF THE PAST",
  18.     "THREE IS COMPANY",
  19.     "A SHORT CUT TO MUSHROOMS",
  20.     "A CONSPIRACY UNMASKED",
  21.     "THE OLD FOREST",
  22.     "IN THE HOUSE OF TOM BOMBADIL",
  23.     "FOG ON THE BARROW-DOWNS",
  24.     "AT THE SIGN OF THE PRANCING PONY",
  25.     "STRIDER",
  26.     "A KNIFE IN THE DARK",
  27.     "FLIGHT TO THE FORD", #Chapter 12
  28.     "MANY MEETINGS",
  29.     "THE COUNCIL OF ELROND",
  30.     "THE RING GOES SOUTH",
  31.     "A JOURNEY IN THE DARK",
  32.     "THE BRIDGE OF KHAZAD-DUˆ M",
  33.     "LOTHLO´ RIEN",
  34.     "THE MIRROR OF GALADRIEL",
  35.     "FAREWELL TO LO´ RIEN",
  36.     "THE GREAT RIVER",
  37.     "THE BREAKING OF THE FELLOWSHIP",
  38. ]
  39.  
  40. indexes = [80513, 135385, 195047, 246726, 277838, 305571, 340779, 370761, 406890, 440901, 471795, 522770, 569883, 618489, 704484, 761559, 823374, 852567, 902818, 938981, 971921, 1010478]
  41. chapters_idx_in_text = {}
  42. for i in range(0, len(chapters_titles_in_pfd)):
  43.     if i == len(chapters_titles_in_pfd)-1:
  44.         chapters_idx_in_text[chapters_titles_in_pfd[i]] = (indexes[i], -1)
  45.     else:
  46.         chapters_idx_in_text[chapters_titles_in_pfd[i]] = (indexes[i], indexes[i + 1])
  47.  
  48. #print(chapters_idx_in_text)
  49. url = 'https://the-one-api.dev/v2/character'
  50. from requests.structures import CaseInsensitiveDict
  51. headers = CaseInsensitiveDict()
  52. headers["Accept"] = "application/json"
  53. headers["Authorization"] = "Bearer 7mbNq6GVWo0LiEQS7GCc"
  54. get_characters = requests.get(url, headers=headers)
  55. if get_characters.status_code == requests.codes.ok:
  56.     api_characters = get_characters.json()
  57.  
  58. #Делаем API запрос, чтобы узнать все имена персонажей из "Вдастелина колец"
  59. all_characters = []
  60. for one in api_characters["docs"]:
  61.     all_characters.append(one["name"])
  62. #Узнаем, какие имена встречаются в тексте
  63. heroes_in_text = []
  64. for hero in all_characters:
  65.     if hero in txt_list:
  66.         heroes_in_text.append(hero)
  67.  
  68.  
  69.  
  70. #Будем называть ИНДЕКСОМ ГЕРОЯ каждый индекс вхождения слова в список txt_list -- список всех слов, разделённых пробелом
  71. #Например:
  72. # имя героя -- 'a'
  73. #txt_list = ['a','b','c','a']
  74. #Тогда 0 и 3 -- являются ИНДЕКСАМИ героя
  75.  
  76.  
  77. #Функции
  78. #Посчитать расстояние между 2-мя индексами
  79. def dst(a, b):
  80.     return abs(a - b)
  81.  
  82.  
  83.  
  84. #Посчитать расстояние между одним индексом героя A и всеми индексами героя B
  85. def binP_dst(x, array):
  86.     L = 0
  87.     R = len(array) - 1
  88.     while R - L > 1:
  89.         idx = (L + R) // 2
  90.         y = array[idx]
  91.         if y >= x:
  92.             R = idx
  93.             L = (L + R) // 2
  94.         else:
  95.             L = idx
  96.             R = (R + len(array)) // 2
  97.     if dst(x, array[L]) <=  dst(x, array[R]):
  98.         return dst(x, array[L])
  99.     else:
  100.         return dst(x, array[R])
  101.  
  102.  
  103.  
  104. heroes_idx = {}
  105.  
  106.  
  107. #Посчитать минимальное расстояние между именами 2-х героев в тексте
  108. def min_dist(hero_A, hero_B):
  109.     min_dst = pow(10, 10)
  110.     idx_A = heroes_idx[hero_A]
  111.     idx_B = heroes_idx[hero_B]
  112.     for i in range(len(idx_A)):
  113.         new_dst = binP_dst(idx_A[i], idx_B)
  114.         if new_dst < min_dst:
  115.             min_dst = new_dst
  116.     return min_dst
  117.  
  118.  
  119. #Получить список всех индексов одного персонажа в списке txt_list
  120. def get_idx(hero_name):
  121.     hero = hero_name
  122.     from_ = 0
  123.     all_idx = []
  124.     while hero in txt_list[from_::]:
  125.         idx = txt_list.index(hero, from_)
  126.         all_idx.append(idx)
  127.         from_ = idx+1
  128.     return all_idx
  129.  
  130.  
  131. #Теперь посчитаем все индексы для каждого героя
  132. for hero in heroes_in_text:
  133.     hero_idx = get_idx(hero)
  134.     heroes_idx[hero] = hero_idx
  135.  
  136.  
  137.  
  138.  
  139.  
  140. heroes_dist = {}
  141. for hero in heroes_in_text:
  142.     heroes_dist[hero] = {}
  143.  
  144. for hero_A in heroes_idx:
  145.     for hero_B in heroes_idx:
  146.         if (hero_A != hero_B) and (hero_B not in heroes_idx[hero_A]) and (hero_A not in heroes_idx[hero_B]):
  147.             minimum_dist = min_dist(hero_A, hero_B)
  148.             heroes_dist[hero_A][hero_B] = minimum_dist
  149.             heroes_dist[hero_B][hero_A] = minimum_dist
  150.  
  151.  
  152. with open("heroes_dist.json", 'w', encoding='utf-8') as fh:
  153.     fh.write(json.dumps(heroes_dist, ensure_ascii=False, indent = 4))
  154.  
  155. #Персонажи не связаны, если количество слов между ними больше N.
  156. #Пусть N = среднеарифметиечское кол-во слов в одной главе, делённое на 80
  157. N = len(txt_list) / len(chapters_titles_in_pfd) / 80
  158. """Обоснование выбора числа N  в задании 4.
  159. Назовём героев "связными", если они вместе принимают участие хотя бы в одном эпизоде.
  160. Глава -- это смысловая часть, в которой происходит какой-то некотрое количество эпизодов.
  161. Значит, если два каких-то персонажа фигурируют в одной и той же главе, то есть вероятность,
  162. что они вместе принимают,участие в каком-то эпизоде.
  163.  
  164. Чтобы как можно более точно отразить связть персонажей между собой, разделим
  165. средний размер главы на 80, предпологая, что в одной главе может быть до 80 эпизодов.
  166. Теперь после деления на 80 вероятность того, что персонажи принимают участие в одном эпиходе, значительно возросла.
  167.  
  168. В итоге вышло, что N = 110 (примерно).
  169. Больше этого значения N лучше не брать, поскольку, чем больше N, тем более запутанный и визуально непонятный граф.
  170. """
  171.  
  172. #Строим граф
  173. import networkx as nx
  174. import matplotlib.pyplot as plt
  175. from random import randint
  176. G = nx.Graph()
  177. marked_edges = []
  178. colors = ['red', 'green', 'pink',  'brown', 'yellow', 'purple', 'blue']
  179.  
  180. for hero_A in heroes_in_text:
  181.     for hero_B in heroes_dist[hero_A]:
  182.         if heroes_dist[hero_A][hero_B] <= N:
  183.             if min(hero_A, hero_B) + max(hero_A, hero_B) not in marked_edges:
  184.                 marked_edges.append(min(hero_A, hero_B) + max(hero_A, hero_B))
  185.                 G.add_edge(hero_A, hero_B, weight = heroes_dist[hero_A][hero_B])
  186. color_map = []
  187. cnt = -1 #сделаем рандомные цвета для всех вершин, чтобы было легче различать разных героев
  188. for edge in G:
  189.     cnt += 1
  190.     cnt %= len(colors)
  191.     color_map.append(colors[cnt])
  192. color_map_2 = []
  193. cnt = -1
  194. for node in G:
  195.     cnt += 1
  196.     cnt %= len(colors)
  197.     color_map_2.append(colors[cnt])
  198. #Разноцветные рёбра в графе могут помочь лучше проследить связть между 2-мя персонажами
  199. nx.draw(G, with_labels=True,node_color= color_map_2,edge_color = color_map)#,  node_size = 10
  200. #plt.savefig("N4_22.png")
  201. plt.show()
  202.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement