Я подумал, что должен расширить свой комментарий на более длинный ответ, поскольку некоторые вещи здесь заслуживают объяснения и разъяснения для будущих читателей.
В ваш ответ Вы написали:
Я понял свою ошибку. 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
Однако в этом все еще нет необходимости, потому что, как я упоминал в своем комментарии и объяснял далее, в документации a 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
s с использованием наиболее подходящего измерения длины (в данном случае Мпк, поскольку это то, что вы указали в своих расстояниях). Однако сам по себе 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
или 111ms для 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