@TomRiddle
Я здесь, потому что ничего не нагуглил XD

Как можно ускорить работу данной программы?

Цель - написать программу которая будет анализировать детализацию звонков, и в том числе выдавать название сотового оператора и его регион по искомому абонентскому номеру.

Нашел на просторах интернета файлы с данными сотовых операторов и провайдеров IP-телефонии, где информация предоставлена в следующем формате:
АВС/ DEF;     От;          До;         Емкость;       Оператор;           Регион
900;         0000000;      0061999;     62000;     ООО "Т2 Мобайл";       Краснодарский край
900;         0062000;      0062999;     1000;       ООО "Т2 Мобайл";       Ростовская обл.
900;         0063000;      0099999;     37000;     ООО "Т2 Мобайл";       Краснодарский край

и таких 4 файла по 8000, 12000, 16000 и 28000 строк соответственно.

Решил создать словарь с данными по следующему типу:
data = {
"900" : {
	((str(j) for j in range(int(0), int(61999) + 1)), 'ООО "Т2 Мобайл"', 'Краснодарский край'),
	((str(j) for j in range(int(62000), int(62999) + 1)), 'ООО "Т2 Мобайл"', 'Ростовская обл.'),
	((str(j) for j in range(int(63000), int(99999) + 1)), 'ООО "Т2 Мобайл"', 'Краснодарский край')
        },
"901" : {
	((str(j) for j in range(int(0), int(9999) + 1)), 'ООО "Т2 Мобайл"', 'Ростовская обл.'),
	((str(j) for j in range(int(10000), int(19999) + 1)), 'ООО "Т2 Мобайл"', 'Республика Северная Осетия - Алания'),
	((str(j) for j in range(int(20000), int(99999) + 1)), 'ООО "Т2 Мобайл"', 'Краснодарский край'),
	((str(j) for j in range(int(100000), int(134999) + 1)), 'ООО "Т2 Мобайл"', 'Республика Адыгея'),
	((str(j) for j in range(int(135000), int(135049) + 1)), 'ООО "Т2 Мобайл"', 'Пензенская обл.')
        }
}

Путем дальнейшего обращения к ключу и перебору его значений до момента пока не найдется нужное таки получается определить оператора и регион по искомому абонентскому номеру, но есть проблема: из-за большого объема данных эта функция выполняется примерно:
8-12 секунд для номеров по типу 900-1234567
12-15 секунд для номеров по типу 812-1234567
15-20 секунд для номеров по типу 345-1234567
свыше 20 секунд для номеров по типу 495-1234567...

Если учесть, что в детализации звонков будет порядка 100-1000 номеров (а то и больше), которые нужно проанализировать, то программа зависнет на очень долго..

Отсюда вопрос - как можно сократить время работы?? Может как-то изменить алгоритм или подключить дополнительные библиотеки? Или есть принципиально другой вариант решения поставленной подзадачи???
Думал над API, но у них есть ограничения по общему количеству запросов, а также по количеству запросов в секунду, поэтому этот вариант отпал..

Надеюсь на Вашу помощь, заранее спасибо!
  • Вопрос задан
  • 183 просмотра
Решения вопроса 1
shabelski89
@shabelski89
engineer
Всё уже объяснили, но от нечего делать положу на блюдечке:
получаем файлы с россвязи и загружаем их в БД
spoiler
import requests
import os
import urllib3
import csv
import sqlite3


source = r'https://rossvyaz.gov.ru/data/'
abc3 = 'ABC-3xx.csv'
abc4 = 'ABC-4xx.csv'
abc8 = 'ABC-8xx.csv'
def9 = 'DEF-9xx.csv'
path = os.getcwd() + '\\NP\\'
db = path + "np.db"
try:
    os.mkdir(path)
except OSError:
    pass

file_list = [abc3, abc4, abc8, def9]


table = "CREATE TABLE numbering_plan(prefix INT, begin INT, end INT, capacity INT, operator TEXT, region TEXT);"
with sqlite3.connect(db) as connection:
    cursor = connection.cursor()
    cursor.execute(table)


for file in file_list:
    urllib3.disable_warnings()
    r = requests.get(source + file, verify=False)
    open(path + file, 'wb').write(r.content)
    with open(path + file, 'r', encoding='utf-8') as f:
        dr = csv.DictReader(f, delimiter=';', quoting=csv.QUOTE_NONE)
        to_db = [(i['АВС/ DEF'], i['От'], i['До'], i['Емкость'], i['Оператор'], i['Регион']) for i in dr]
        with sqlite3.connect(db) as connection:
            cursor = connection.cursor()
            cursor.executemany("INSERT INTO numbering_plan (prefix, begin, end, capacity, operator, region) "
                               "VALUES (?, ?, ?, ?, ?, ?);", to_db)



далее уже все зависит от Вашей обертки ниже код для проверки, опять же коллеги выше это уже писали

spoiler
import sqlite3
import os


path = os.getcwd() + '\\NP\\'
db = path + "np.db"

num = 1

while num:
    num = input('Введите номер в формате ABD/DEАххх ')
    prefix = num[:3]
    number = num[3:]
    search_query = 'SELECT * FROM numbering_plan WHERE prefix=? AND ? BETWEEN begin AND end;'

    with sqlite3.connect(db) as connection:
        cursor = connection.cursor()
        cursor.execute(search_query, (prefix, number))
        result = cursor.fetchall()
    print(result)


скачка и загрузка файлов БД меньше минуты, поиск меньше секунды
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 5
fox_12
@fox_12 Куратор тега Python
Расставляю биты, управляю заряженными частицами
По-моему дичь какую-то придумали...
Сохраните в базу данных, да делайте выборки....

что-то вроде:
PhoneOperator:
    code = CharField()
    name = 
    region = 

PhoneRange:
   minval = BigIntegerField()
   maxval = BigIntegerField()
   operator = ForeignKey(PhoneOperator)

Тогда
my_operator = PhoneOperator.objects.filter(
    code=my_number[0:3],
    phonerange__minval__lte=my_number[3:],
    phonerange__maxval__gte=my_number[3:]
)
Ответ написан
shurshur
@shurshur
Сисадмин, просто сисадмин...
Знакомый формат, файлы эти явно из выгрузок Россвязи. Я через такие данные прогонял десятки миллионов номеров, причём задачи сделать быстро и эффективно у меня не было.

Если не использовать базы данных, то можно просто сохранить диапазоны в массиве, отсортированными по началу диапазона (файлы уже в таком виде), и прерывать обработку, когда пройдём нужное место:

prefix_code = '495'
number = '1234567'

for row in data_rows[prefix_code]:
  range_start, range_end, range_size, range_owner, range_region = row
  if number < range_start:
    continue # переходим к следующему
  if number <= range_end:
    print (f"Found {range_owner}, {range_region}")
  else:
    print ("Not found")
  break


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

Если через базу, то просто сделать запрос в стиле (тут всё в числа переведено, строки не нужны):

SELECT * FROM ranges_data WHERE prefix_code=495 AND 1234567 BETWEEN range_start AND range_end;
Ответ написан
NeiroNx
@NeiroNx
Программист
Думаю надо просто в число преобразовывать номер и сравнивать. Числа то проще сравнить чем массивы гонять или нет?
Ответ написан
Комментировать
Tomio
@Tomio
backend developer (python, php)
Самое оптимальное вам посоветовали - это база данных с созданными индексами по колонкам, в которых вы будете искать. Быстрее способа что-то найти вы не найдете.
Ну раз упомянули базу данных, посоветую вам еще библиотеку pandas - это прям вообще мощный монстр, который сможет вам очень сильно упростить процедуру поиска данных в таблице. Просто скармливаете пандасу ваш csv файл и гуглите нужные методы для ваших целей) ибо у него их полно.
Ответ написан
Комментировать
@TomRiddle Автор вопроса
Я здесь, потому что ничего не нагуглил XD
Всем спасибо за ответы
Изучил sqlite3, вопрос решился ✊
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы