Как оптимизировать скрипт для наилучшего использования оперативной памяти?

Всем привет) Я написал скрипт. Но, из-за того, что скрипт генерит очень большие объемы данных, на определенном этапе, он вылетает. Причиной этого есть то, что оперативная память переполненная.

Помогите пожалуйста оптимизировать скрипт для наилучшего использования оперативной памяти.

Что в общих чартах делает скрипт:

  1. Я задаю координаты (lat_1, lat_2, long_1, long_2) для большого квадрата.

  2. На основании этих координат, скрипт строит квадраты размером в 100*100 метров. Каждый квадрат имеет 4 координаты (longtitude, latitude). Получается так, как на рисунке. Все это складывается в файл geojson введите сюда описание изображения

Общая логика такая:

Сначала генерю список lattitute / longtitude c шагом 0.000001.

# Generate the list of geo-locations

def geo_gen(start, finish, step = 0.000001):
    geo = []
    k = start
    for i in range(1, int((finish - start)/step)+2):
        start+=step
        geo.append(start)
    return geo

Потом для каждого lattitute / longtitude определяю размер в зависимости от положения на карте

# Generate distance based on geo 
def distance_generate(geo, step, type_):
    distance = []
    equator_step = 111320 * step
    if type_ == 'longitude':
        for i in geo:
            result = cos(radians(i))*equator_step
            distance.append([i, result])
    else: 
        if type_ == 'latitude':
            for i in geo:
                result = equator_step
                distance.append([i, result])
    return distance

Сохраняю все это в dataframe

# Generate files 
def generate_file(lst, filename):
    pd.DataFrame(lst).to_csv(filename, header=['geo', 'distance'], index=False)

Генерю списки longtitude / latitude с учетом того, что расстояние между соседними точками должно быть 100 метров.

 # generate latitude and longitude lists with the length of 30 meters
    def gen_squares(data, distance=100):
        summa = 0
        list_ = []
        for i, k in zip(data['geo'], data['distance']):
            summa +=k
            if summa>distance:
                list_.append(i)
                summa=0
        return list_

Генерю нужные мне квадраты

   def polygon_generate(s, k):
        arr = []
        for i in range(0, len(s)-1):
            for j in range(0,len(k)-1):
                arr.append([[round(s[i], 6),round(k[j],6)],[round(s[i+1],6),round(k[j],6)],[round(s[i+1],6),round(k[j+1],6)],[round(s[i],6),round(k[j+1],6)]])
        return arr

Генерю JSON и сохраняю данные

def geo_json(list):
    list_json = []
    z = 0
    for key in list:
        zc = z+1
        string = '{"type": "Feature", "properties": {"area": '+str(zc)+'}, "geometry": {"type": "Polygon", "coordinates": [['+str(list[z][0])+','+str(list[z][1])+','+str(list[z][2])+','+str(list[z][3])+']]}}'
        z += 1
        list_json.append(json.loads(string))
    print('JSON loads generated')
    json_1 = {"type": "FeatureCollection", "features":list_json}
    print('JSON is created')
    with open(filename_json, 'w') as f:
        json.dump(json_1, f, indent=None, separators=(",", ":"))
    print('The file is stored')

Полный скрипт с применением выглядит так:

from math import cos, radians
import pandas as pd
import json

country = 'Indonesia'
lat_1 = 90
lat_2 = 140
long_1 = -10
long_2 = 10

filename_long = f'{country}_long_{long_1}-{long_2}.csv'
filename_lat = f'{country}_lat_{lat_1}-{lat_2}.csv'
filename_json = f'{country}_LAT_{lat_1}_{lat_2}_LONG_{long_1}_{long_2}.geojson'

# Generate the list of geo-locations
def geo_gen(start, finish, step = 0.000001):
    geo = []
    k = start
    for i in range(1, int((finish - start)/step)+2):
#         print(start)
        start+=step
        geo.append(start)
    return geo

# Generate distance based on geo 
def distance_generate(geo, step, type_):
    distance = []
    equator_step = 111320 * step
    if type_ == 'longitude':
        for i in geo:
            result = cos(radians(i))*equator_step
            distance.append([i, result])
    else: 
        if type_ == 'latitude':
            for i in geo:
                result = equator_step
                distance.append([i, result])
    return distance

# Generate files 
def generate_file(lst, filename):
    pd.DataFrame(lst).to_csv(filename, header=['geo', 'distance'], index=False)


# generate latitude and longitude lists with the length of 30 meters
def gen_squares(data, distance=30):
    summa = 0
    list_ = []
    for i, k in zip(data['geo'], data['distance']):
        summa +=k
        if summa>distance:
            list_.append(i)
            summa=0
    return list_

def polygon_generate(s, k):
    arr = []
    for i in range(0, len(s)-1):
        for j in range(0,len(k)-1):
            arr.append([[round(s[i], 6),round(k[j],6)],[round(s[i+1],6),round(k[j],6)],[round(s[i+1],6),round(k[j+1],6)],[round(s[i],6),round(k[j+1],6)]])
    return arr

def geo_json(list):
    list_json = []
    z = 0
    for key in list:
        zc = z+1
        string = '{"type": "Feature", "properties": {"area": '+str(zc)+'}, "geometry": {"type": "Polygon", "coordinates": [['+str(list[z][0])+','+str(list[z][1])+','+str(list[z][2])+','+str(list[z][3])+']]}}'
        z += 1
        list_json.append(json.loads(string))
    print('JSON loads generated')
    json_1 = {"type": "FeatureCollection", "features":list_json}
    print('JSON is created')
    with open(filename_json, 'w') as f:
        json.dump(json_1, f, indent=None, separators=(",", ":"))
    print('The file is stored')

# Generate longtitude file
s = geo_gen(long_1, long_2, step = 0.000001)
k = distance_generate(s, step = 0.000001, type_ = 'longitude')
generate_file(k, filename_long)

# Generate lattitude file 
s = geo_gen(lat_1, lat_2, step = 0.000001)
k = distance_generate(s, step = 0.000001, type_ = 'latitude')
generate_file(k, filename_lat)

# Read data from files 
data_long = pd.read_csv(filename_long)
data_lat = pd.read_csv(filename_lat)
print(data_long.shape, data_lat.shape)

# Generate lists 
lat_lst = gen_squares(data_lat, distance=30)
long_lst = gen_squares(data_long, distance=30)
print(len(lat_lst), len(long_lst))

# Generate polygon values
final = polygon_generate(lat_lst, long_lst)
print(len(final))

geo_json(final)

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


Ответы (1 шт):

Автор решения: gil9red

Я бы попробовал возвращать генераторы вместо списков в функциях.

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

Пример:

from math import cos, radians
from typing import Generator
import pandas as pd
import json

country = 'Indonesia'
lat_1 = 106
lat_2 = 120
long_1 = -10
long_2 = -10  # gil9red: Пусть такое будет значение
STEP = 0.000001
STEP = 0.001  # gil9red: Задал меньший шаг, ибо слишком долго ждать

filename_long = f'{country}_long_{long_1}-{long_2}.csv'
filename_lat = f'{country}_lat_{lat_1}-{lat_2}.csv'
filename_json = f'{country}_LAT_{lat_1}_{lat_2}_LONG_{long_1}_{long_2}.geojson'

# Generate the list of geo-locations
def geo_gen(start, finish, step=STEP) -> Generator:
    for i in range(1, int((finish - start)/step)+2):
        start += step
        yield start

# Generate distance based on geo
def distance_generate(geo, step, type_) -> Generator:
    equator_step = 111320 * step
    if type_ == 'longitude':
        for i in geo:
            result = cos(radians(i))*equator_step
            yield [i, result]
    else:
        if type_ == 'latitude':
            for i in geo:
                result = equator_step
                yield [i, result]

# Generate files
def generate_file(lst, filename):
    pd.DataFrame(lst).to_csv(filename, header=['geo', 'distance'], index=False)


# generate latitude and longitude lists with the length of 30 meters
def gen_squares(data, distance=100):
    summa = 0
    list_ = []
    for i, k in zip(data['geo'], data['distance']):
        summa += k
        if summa > distance:
            list_.append(i)
            summa = 0
    return list_

def polygon_generate(s, k) -> Generator:
    for i in range(0, len(s)-1):
        for j in range(0,len(k)-1):
            yield [[round(s[i], 6),round(k[j],6)],[round(s[i+1],6),round(k[j],6)],[round(s[i+1],6),round(k[j+1],6)],[round(s[i],6),round(k[j+1],6)]]

def geo_json(items):
    list_json = []
    for i, x in enumerate(items):
        string = '{"type": "Feature", "properties": {"area": '+str(i + 1)+'}, "geometry": {"type": "Polygon", "coordinates": [['+str(x[0])+','+str(x[1])+','+str(x[2])+','+str(x[3])+']]}}'
        list_json.append(json.loads(string))

    print('JSON loads generated')
    json_1 = {"type": "FeatureCollection", "features":list_json}
    print('JSON is created')
    with open(filename_json, 'w') as f:
        json.dump(json_1, f, indent=None, separators=(",", ":"))
    print('The file is stored')

# Generate longtitude file
print('Generate longtitude file')
s = geo_gen(long_1, long_2, step=STEP)
k = distance_generate(s, step=STEP, type_ = 'longitude')
generate_file(k, filename_long)

# Generate lattitude file
print('Generate lattitude file')
s = geo_gen(lat_1, lat_2, step=STEP)
k = distance_generate(s, step=STEP, type_ = 'latitude')
generate_file(k, filename_lat)

# Read data from files
print('Read data from files')
data_long = pd.read_csv(filename_long)
data_lat = pd.read_csv(filename_lat)
print(data_long.shape, data_lat.shape)

# Generate lists
print('Generate lists')
lat_lst = gen_squares(data_lat, distance=30)
long_lst = gen_squares(data_long, distance=30)
print(len(lat_lst), len(long_lst))

# Generate polygon values
print('Generate polygon values')
final = polygon_generate(lat_lst, long_lst)
# print(len(final))

print('geo_json(final)')
geo_json(final)
→ Ссылка