Как оптимизировать скрипт для наилучшего использования оперативной памяти?
Всем привет) Я написал скрипт. Но, из-за того, что скрипт генерит очень большие объемы данных, на определенном этапе, он вылетает. Причиной этого есть то, что оперативная память переполненная.
Помогите пожалуйста оптимизировать скрипт для наилучшего использования оперативной памяти.
Что в общих чартах делает скрипт:
Я задаю координаты (lat_1, lat_2, long_1, long_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 шт):
Я бы попробовал возвращать генераторы вместо списков в функциях.
Не получилось перевести на генераторную реализацию 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)