Обработка очень большого массива с помощью Astropy и numpy

По некоторым причинам мне нужно использовать астропию для преобразования сопутствующего расстояния в красное смещение. В основном это включает в себя чтение, просмотр и запись списка или массива numpy ... Моя проблема в том, что каждый из моих списков обычно состоит из ~ 9,5 x 10 ^ 6 элементов. И это вызывает ошибку MemoryError каждый раз, когда я пытаюсь сохранить вывод в новый текстовый файл с помощью numpy.savetxt. Использование памяти быстро растет, в конечном итоге немного замедляется, но всегда превышает установленный у меня предел 128 ГБ.

Если кто-нибудь знает, как я могу улучшить приведенный ниже сценарий, я очень хочу его выслушать. Спасибо!

import os
import sys
import glob
import math
import numpy
import astropy
import astropy.units as unit
from astropy.cosmology import *
cosmo = FlatLambdaCDM(H0=70, Om0=0.3)

inFile=sys.argv[1]
outFile=sys.argv[2]

comovingDistance = numpy.loadtxt(inFile, usecols=(2,))

Redshift = numpy.zeros(len(comovingDistance)) 
for i in range(len(comovingDistance)):
    Redshift[i] = z_at_value(cosmo.comoving_distance, comovingDistance[i] * unit.kpc)

output = open(outFile,'w')
numpy.savetxt(output, Redshift, fmt='%1.8e')
output.close()

Ниже представлен файл журнала ошибок:

Traceback (most recent call last):
  File "comoving2redshift.py", line 21, in <module>
    Redshift[i] = z_at_value(cosmo.comoving_distance, comovingDistance[i] * unit.kpc)
  File "/afs/mpa/home/minh/.local/lib/python2.7/site-packages/astropy/cosmology/funcs.py", line 119, in z_at_value
    fval_zmax = func(zmax)
  File "/afs/mpa/home/minh/.local/lib/python2.7/site-packages/astropy/cosmology/core.py", line 1195, in comoving_distance
    return self._comoving_distance_z1z2(0, z)
  File "/afs/mpa/home/minh/.local/lib/python2.7/site-packages/astropy/cosmology/core.py", line 1219, in _comoving_distance_z1z2
    return self._hubble_distance * vectorize_if_needed(f, z1, z2)
  File "/afs/mpa/home/minh/.local/lib/python2.7/site-packages/astropy/units/quantity.py", line 924, in __mul__
    return super(Quantity, self).__mul__(other)
  File "/afs/mpa/home/minh/.local/lib/python2.7/site-packages/astropy/units/quantity.py", line 368, in __array_prepare__
    from .quantity_helper import UNSUPPORTED_UFUNCS, UFUNC_HELPERS
MemoryError

person Minh N    schedule 25.04.2017    source источник
comment
На какой линии вы получаете MemoryError?   -  person Divakar    schedule 25.04.2017
comment
@Divakar Я добавил файл журнала ошибок в вопрос ...   -  person Minh N    schedule 25.04.2017
comment
Примечание в документах к z_at_value кажется относящимся к в вашем случае, вы можете попробовать этот подход. Кроме того, вы пытались проверить, векторизован ли z_at_value? Весьма вероятно, что вам не нужно повторять себя, и вы можете просто выполнить Redshift = z_at_value(cosmo.comoving_distance, comovingDistance * unit.kpc) без необходимости выделять массив Redshift.   -  person Jaime    schedule 25.04.2017
comment
@Evert Спасибо! Я тоже думал об этом, и, собственно, я так и сделал, потому что, кажется, нет более умного способа.   -  person Minh N    schedule 26.04.2017
comment
@Jaime Спасибо за уведомление об астропийном документе. Я прочитал его очень быстро и пропустил часть о вычислении многих величин с использованием той же космологии. Действительно, положение спасает интерполяция.   -  person Minh N    schedule 26.04.2017


Ответы (2)


Я не знаю никакого решения, присущего numpy, но вы можете сэкономить выделение памяти, записав каждое решение сразу в файл, а не после цикла for. Это сохраняет выделение памяти для Redshift и выделение памяти, выполняемое за кулисами, когда numpy.savetxt() форматирует числа с плавающей запятой в строку.

inFile=sys.argv[1]
outFile=sys.argv[2]

comovingDistance = numpy.loadtxt(inFile, usecols=(2,))

with open(outFile, 'w') as fp:
    for distance in comovingDistance:
        fp.write("{:1.8e}\n".format(
            z_at_value(cosmo.comoving_distance, distance * unit.kpc)))

(NB: не проверено)

person Community    schedule 26.04.2017

В качестве альтернативы другому предлагаемому мной решению вы можете разделить входной файл, перебрать новый набор (временных) входных файлов и в конце объединить входные файлы. Ниже приведен сценарий оболочки bash, который снаружи должен работать идентично сценарию Python в вопросе (один аргумент входного файла, один аргумент выходного файла).

#! /bin/bash                                                                                   

nlines=10000                                                                                   
input=$1                                                                                       
output=$2                                                                                      

# use a unique prefix!                                                                         
prefix='tmpsplit'                                                                              
split --lines=$nlines $input $prefix                                                           

outfiles=()                                                                                    
# Assume we only split to a maximum of 26^2 files
# This is the default for split anyway                                              
for filename in ${prefix}??                                                                    
do                                                                                             
        outfile="${filename}-out"                                                              
        ./calcdist.py $filename $outfile                                                      
done                                                                                           

# This assumes the shells orders the glob expansion alphabetically                             
cat ${prefix}*out > $output                                                                    

# Clean up                                                                                     
rm ${prefix}*                                                                                  

Вы можете использовать временный каталог вместо того, чтобы полагаться на уникальный префикс.

person Community    schedule 26.04.2017