Ошибка числового значения: установка элемента массива с последовательностью

Я пишу код на Python, используя numpy и astropy. В коде я хочу создать случайные массивы, которые обычно напоминают мои наборы данных. После этого я хотел бы преобразовать эти рандомизированные массивы сферических координат в декартовы. К сожалению, я продолжаю получать ошибку значения, я полностью озадачен тем, почему это происходит, я пытался сделать несколько фиктивных тестов, таких как, если они имеют одинаковую форму, все массивы имеют приемлемые значения и того же типа и т. Д., Но Я застрял. вот мой код:

from astropy.coordinates import SkyCoord
from astropy import units as u
import numpy as np 

R   = 445 + np.random.randn(262615) 
print(np.shape(R))
dec = 2 + np.random.randn(262615)
print(np.shape(dec))
ra  = 150 + np.random.randn(262615)
print(np.shape(ra))
c   = np.zeros(262615) 
print(np.shape(c))


for i in range(262615):
    c[i] = SkyCoord(ra=ra[i]*u.degree,dec=dec[i]*u.degree,distance=R[i]*u.mpc)
    print(c[i])      

вот мое сообщение об ошибке:

PS C:\Users\sirep\Documents\C++ scripts> cd 'c:\Users\sirep\Documents\C++ scripts'; ${env:PYTHONIOENCODING}='UTF-8'; ${env:PYTHONUNBUFFERED}='1'; & 'C:\Users\sirep\Anaconda3\python.exe' 'c:\Users\sirep\.vscode\extensions\ms-python.python-2018.5.0\pythonFiles\PythonTools\visualstudio_py_launcher.py' 'c:\Users\sirep\Documents\C++ scripts' '57764' '34806ad9-833a-4524-8cd6-18ca4aa74f14' 'RedirectOutput,RedirectOutput' 'c:\Users\sirep\Documents\Python Scripts\sph2cart.py'
(262615,)
(262615,)
(262615,)
(262615,)
Traceback (most recent call last):
  File "c:\Users\sirep\Documents\Python Scripts\sph2cart.py", line 16, in <module>
    c[i] = SkyCoord(ra=ra[i]*u.degree,dec=dec[i]*u.degree,distance=R[i]*u.mpc)
ValueError: setting an array element with a sequence.

Спасибо всем за ваше время!

2 ответа

Решение

Я подумал, что должен расширить свой комментарий до более длинного ответа, поскольку некоторые вещи здесь заслуживают объяснения и разъяснения для будущих читателей.

В своем ответе вы написали:

Я понял свою ошибку. SkyCoord возвращает 3 значения: координату x, координату y и координату z. Я пытался присвоить три значения одному элементу массива.

и это, конечно, на правильном пути, но не совсем. В исходном коде у вас было что-то вроде:

c = np.zeros(262615)

Это уже вызывает проблемы, так как вы не указали тип данных, но по умолчанию тип данных float64 что, вероятно, то, что вы хотите для многих приложений (конечно, верно для этого). В любом случае, типизированные массивы Numpy означают, что если вы присваиваете одному элементу массива, как в исходном коде:

c[i] = SkyCoord(ra=ra[i]*u.degree,dec=dec[i]*u.degree,distance=R[i]*u.mpc)

назначаемое вами значение должно быть числом с плавающей запятой или, по крайней мере, каким-либо другим числовым типом (например, int) которые могут быть однозначно преобразованы в плавающие. Это не правда для SkyCoord поскольку, как вы заметили, это мультиплет трех измерений. Я хочу сказать, что в целом, если вы используете массивы Numpy, вы должны быть осторожны с dtype и что вы пытаетесь присвоить элементу этого. Для более произвольных объектов вы, скорее всего, получите немного более ясную ошибку, например:

>>> c[0] = object()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: float() argument must be a string or a number

Все еще не велик, но по крайней мере это демонстрирует, что он пытается позвонить float() преобразовать аргумент в число с плавающей точкой. Но вы получите другой результат для SkyCoord поскольку SkyCoord может быть контейнером для массива многих координат, и Numpy видит это и вместо этого пытается обработать его, как будто вы присваиваете последовательность значений в скаляре, что является ошибкой, которую вы получаете.

Кстати, в Numpy также возможно создавать более сложные типы массивов, используя структурированные массивы. Это позволяет вам создать массив (x, y, z) координаты например как:

>>> c = np.zeros(262615, dtype=[('x', 'f8'), ('y', 'f8'), ('z', 'f8')])
>>> c
array([(0.0, 0.0, 0.0), (0.0, 0.0, 0.0), (0.0, 0.0, 0.0), ...,
       (0.0, 0.0, 0.0), (0.0, 0.0, 0.0), (0.0, 0.0, 0.0)],
      dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])
>>> c[0]
(0.0, 0.0, 0.0)

Хотя вы не можете назначить SkyCoord непосредственно в одно из этих значений (я думаю, технически SkyCoord рассматривается как свободный от координат, независимо от того, какую систему координат вы использовали для его создания, но я могу ошибаться в этом), вы можете назначить, например:

>>> c[0] = SkyCoord(ra=ra[i]*u.degree,dec=dec[i]*u.degree,distance=R[i]*u.mpc).cartesian.xyz

Тем не менее, это все еще не является необходимым, потому что, как я упоминал в своем комментарии и объяснил далее в документах, SkyCoord может представлять массив координат как:

>>> coords = SkyCoord(ra=ra*u.degree, dec=dec*u.degree, distance=R*u.mpc)

и вы можете преобразовать все это за один раз в декартовы координаты и получить отдельные массивы для координат x, y и z, например:

>>> x, y, z = coords.cartesian.xyz

Это имеет дополнительное преимущество, что координаты возвращаются как Quantity Использует наиболее подходящий размер длины (в данном случае Мпк, так как это то, что вы дали ваши расстояния). Тем не мение, coords.cartesian сам по себе уже эффективно массив (x, y, z) координаты очень похожи на мой пример структурированного массива выше (технически это не массив Numpy, но он имеет много одинаковых методов, может быть преобразован в один подобный):

>>> coords.cartesian._values
array([(0.19718680211339326, 0.002173755110841713, 0.0021735811221131776),
       (0.6853033697941637, 0.005924402286034272, 0.004262079913938389),
       ...
      dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])

но это недокументированный внутренний атрибут, который не должен использоваться (хотя я не уверен, почему этот интерфейс не отображается, поскольку он может быть полезен...)

Наконец, я добавлю, что использование этого интерфейса намного, намного быстрее, потому что все циклы - это векторизованные операции с массивами, в основном на C. Каждый раз, когда вы делаете вещи на уровне Python, например, присваиваете массиву (c[i] = ...) или атрибут доступа (c.cartesian.x.value) вы подвергаетесь значительному снижению производительности, так как значения должны быть преобразованы из C в Python и обратно в C. Использование векторизованных операций позволяет избежать всего этого. Поэтому, когда я делаю SkyCoord массив я получаю:

In [7]: %%timeit
   ...: c = SkyCoord(ra=ra*u.degree, dec=dec*u.degree, distance=R*u.mpc)
   ...: c.cartesian.xyz
   ...:
10 loops, best of 3: 111 ms per loop

или 111мс для 262615 координаты, как в вашем исходном примере. Принимая во внимание, что "наивный" путь заставляет меня:

In [11]: %%timeit
    ...: for i in range(262615):
    ...:     c = SkyCoord(ra=ra[i]*u.degree,dec=dec[i]*u.degree,distance=R[i]*u.mpc)
    ...:     cx[i] = c.cartesian.x.value
    ...:     cy[i] = c.cartesian.y.value
    ...:     cz[i] = c.cartesian.z.value
    ...:

1 loop, best of 3: 18min 26s per loop

Я понял свою ошибку. SkyCoord возвращает 3 значения: координату x, координату y и координату z. Я пытался присвоить три значения одному элементу массива. Чтобы исправить это, я должен был сначала создать 3 отдельных массива для каждой координаты, а затем убедиться, что каждое значение безразмерно при вводе в соответствующие массивы:

from astropy.coordinates import SkyCoord
from astropy import units as u
import numpy as np 


R   = 445 + np.random.randn(262615) 
print(np.shape(R))
dec = 2 + np.random.randn(262615)
print(np.shape(dec))
ra  = 150 + np.random.randn(262615)
print(np.shape(ra))
cx   = np.zeros(262615) 
cy   = np.zeros(262615)
cz   = np.zeros(262615)
print(np.shape(cx))


for i in range(262):
    c = SkyCoord(ra=ra[i]*u.degree,dec=dec[i]*u.degree,distance=R[i]*u.mpc)
    cx[i] = c.cartesian.x.value
    cy[i] = c.cartesian.y.value
    cz[i] = c.cartesian.z.value
    print(cx[i],cy[i],cz[i])
Другие вопросы по тегам