Содержание

Быстрые поэлементные операции | Python: Numpy-массивы

Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Чтобы проанализировать данные, можно провести множество разных операций:

  • Сравнить значения
  • Поискать минимальные и максимальные значения
  • Найти суммы и произведения элементов

И это далеко не полный список всех доступных преобразований. В некоторых случаях вычисления над элементами требуют использования более сложных математических операций и функций. Именно под это заточена библиотека Numpy, которая позволяет не только готовить данные к обработке, но и проводить необходимые вычисления. В этом уроке мы разберемся, как эти вычисления работают и как применять их на практике.

Поэлементные преобразования и укладывание

Numpy помогает ускорить операции и упростить синтаксис — так происходит благодаря поэлементным преобразованиям. Он позволяет оперировать с данными разной размерности. Такой подход называется укладыванием

.

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

Чтобы выполнять арифметические операции со стандартными структурами данных в Python, нужно использовать циклы. Их количество и вложенность зависит от размерности. Numpy работает по-другому — логика и синтаксические конструкции в операциях над массивами остается одинаковой для структур разной размерности. Для оптимизации и повышения качества кода циклы скрыты от пользователя.

Посмотрим на пример ниже. В нем показан ряд операций над одномерным массивом данных и числовым значением, которое поэлементно применяется ко всему массиву:

import numpy as np
# Исходный массив
arr1 = np.array([0, 1, 2, 3, 4, 5, 6, 7])
# Значение для изменения элементов массива
change_array_value = 5
print(arr1 + change_array_value)
# => [ 5  6  7  8  9 10 11 12]
print(arr1 - change_array_value)
# => [-5 -4 -3 -2 -1  0  1  2]
print(arr1 * change_array_value)
# => [ 0  5 10 15 20 25 30 35]
print(arr1 / change_array_value)
# => [0.   0.2 0.4 0.6 0.8 1.  1.2 1.4]

Циклы в примере выше отсутствуют. Как мы уже говорили, в Numpy это называется укладыванием. Укладывание элемента в массив было разобрано на примере вектора и числа. Однако укладывать можно не только один элемент, а любой массив подходящего размера — при условии, если структура большей размерности. Посмотрим на пример прибавления элементов вектора построчно к матрице:

# Добавление вектора к матрице
matrix_array = np.array([[5, 8], [8, 9]])
vector_array = np.array([1, 2])
print(matrix_array + vector_array)
# => [[ 6 10]
#  [ 9 11]]

Чтобы выполнить те же операции над двумя массивами, также не используются циклы. Все синтаксические конструкции остаются без изменений:

# Массив для изменения значений исходного
arr2 = np.array([2, 2, 2, 2, -1, -1, -1, -1])
print(arr1 + arr2)
# => [2 3 4 5 3 4 5 6]
print(arr1 - arr2)
# => [-2 -1  0  1  5  6  7  8]
print(arr1 * arr2)
# => [ 0  2  4  6 -4 -5 -6 -7]
print(arr1 / arr2)
# => [ 0.
0.5 1. 1.5 -4. -5. -6. -7. ]

Для сравнения посмотрим, как выполняются аналогичные задачи над стандартными списками. Без циклов и генератора zip() в этом случае не обойтись:

arr1 = [0, 1, 2, 3, 4, 5, 6, 7]
change_array_value = 5
arr2 = [2, 2, 2, 2, -1, -1, -1, -1]
print([arr1_val + change_array_value for arr1_val in arr1])
# => [5, 6, 7, 8, 9, 10, 11, 12]
print([arr1_val + arr2_val for arr1_val, arr2_val in zip(arr1, arr2)])
# => [2, 3, 4, 5, 3, 4, 5, 6]

Создатели Numpy целенаправленно разработали библиотеку, в которой выполнение функционала не зависит от размерности данных. В качестве примера приведены поэлементные операции над матрицами:

arr1 = np.array([[5, 8], [8, 9]])
arr2 = np.array([[3, 1], [7, 2]])
change_array_value = 3
print(arr1 * arr2)
# => [[15  8]
#  [56 18]]
print(arr1 / change_array_value)
# => [[1.66666667 2.66666667]
#  [2.66666667 3.        ]]

Во всех примерах выше операции с массивами Numpy производились по одному шаблону. Размерность данных не влияла на синтаксис — мы использовали одинаковые математические операторы, меняя только операнды: числа, вектора, матрицы.

Для сравнения изучим пример операций над матрицами, которые представлены стандартными списками. Здесь необходимо использовать циклы:

# Пример для аналогичных операций над стандартными списками
arr1 = [[5, 8], [8, 9]]
arr2 = [[3, 1], [7, 2]]
change_array_value = 3
for i in range(len(arr1)):
    for j in range(len(arr1[0])):
        arr1[i][j] *= arr2[i][j]
print(arr1)
# => [[15, 8],
# [56, 18]]
for i in range(len(arr2)):
    for j in range(len(arr2[0])):
        arr2[i][j] += change_array_value
print(arr2)
# => [[6, 4],
# [10, 5]]

При работе со стандартными списками чем больше размерность, тем больше строк кода. К этому моменту стоит относиться внимательно, ведь длина кода делает его сложнее в поддержке и может приводить к возникновению ошибок.

Как это работает на практике

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

  • Во всей сети магазинов
  • В каждом магазине по отдельности
  • С распределением по дням недели

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

# Продажи магазина
orders =  [
    [7, 1, 7, 8],
    [4, 2, 4, 5],
    [3, 5, 2, 3],
    [8, 12, 8, 7],
    [15, 11, 13, 9],
    [21, 18, 17, 21],
    [25, 16, 25, 17]
]
orders = np.array(orders)

После инициализации данных в виде массива можно перейти к анализу отклонений от среднего по всей сети:

# Среднее значение по всем магазинам по всем дням
mean_orders_value = orders.mean()
print(mean_orders_value)
# => 10.
5 print(orders - mean_orders_value) # => [[-3.5 -9.5 -3.5 -2.5] # [-6.5 -8.5 -6.5 -5.5] # [-7.5 -5.5 -8.5 -7.5] # [-2.5 1.5 -2.5 -3.5] # [ 4.5 0.5 2.5 -1.5] # [10.5 7.5 6.5 10.5] # [14.5 5.5 14.5 6.5]]

Средний показатель для всей сети не всегда подходит для анализа, поскольку у магазинов может быть разный объем продаж. Чтобы лучше понять ситуацию с продажами, найдем среднее по каждому магазину. В примере это среднее значение по столбцам матрицы продаж. Чтобы найти такие средние, используем метод mean() с параметром axis = 0:

# Среднее значение по магазинам
mean_by_shop = orders.mean(axis=0)
print(mean_by_shop)
# => [11.85714286  9.28571429 10.85714286 10.]
print(orders - mean_by_shop)
# =>[[-4.85714286 -8.28571429 -3.85714286 -2.]
#  [-7.85714286 -7.28571429 -6.85714286 -5.]
#  [-8.85714286 -4.28571429 -8.85714286 -7.]
#  [-3.85714286  2.71428571 -2.85714286 -3.]
#  [ 3.14285714  1.71428571  2.14285714 -1.]
#  [ 9.14285714  8.
71428571 6.14285714 11.] # [13.14285714 6.71428571 14.14285714 7.]]

Аналитику также может потребоваться информация о дневных отклонениях. Так, например, можно обнаружить просадку продаж по вине логистов и менеджеров. Для этого необходимо найти средние по дням. К матрице продаж надо применить метод

mean() с параметром axis = 1:

# Среднее значение по дням
mean_by_day = orders.mean(axis=1)
print(mean_by_day)
# => [ 5.75  3.75  3.25  8.75 12.   19.25 20.75]
# Переформатирование вектора для укладывания по столбцам
mean_by_day = mean_by_day.reshape((7,1))
print(mean_by_day)
# => [[ 5.75]
#  [ 3.75]
#  [ 3.25]
#  [ 8.75]
#  [12.  ]
#  [19.25]
#  [20.75]]
print(orders - mean_by_day)
# => [[ 1.25 -4.75  1.25  2.25]
#  [ 0.25 -1.75  0.25  1.25]
#  [-0.25  1.75 -1.25 -0.25]
#  [-0.75  3.25 -0.75 -1.75]
#  [ 3.   -1.    1.   -3.  ]
#  [ 1.75 -1.25 -2.25  1.75]
#  [ 4.25 -4.75  4.25 -3.75]]

В примере выше используется метод reshape() — он помогает преобразовать исходную строку средних в столбец. Это принципиально необходимо для того, чтобы вектор был уложен в матрицу именно по столбцам.

Выводы

В этом уроке мы узнали, что библиотека Numpy упрощает и оптимизирует вычисления с использованием языка Python. Для этого она применяет подход, который унифицирует интерфейс работы с массивами. Все арифметические операции над массивами производятся без циклов — с использованием только самих символов операций.

Фильтрация значений и вырезание | Python: Numpy-массивы

Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

При работе с данными часто возникает необходимость найти элементы по определенному условию. С точки зрения поиска ошибок в данных критерии поиска могут быть разными, например:

  • Отрицательные значения в графе «Количество продаж»
  • Буквы вместо цифр в графе «Номер телефона»
  • Нереалистично большие числа в графе «Сумма сделки»

В стандартном Python искать и фильтровать значения можно с помощью функции filter(). В Numpy есть схожая функциональность, которую мы рассмотрим в этом уроке. Вы узнаете, как получить элементы по заданному условию при работе с массивами numpy.ndarray.

Как создать булеву маску

Для фильтрации значений массива numpy.ndarray по определенному условию используют булевы маски — массивы значений True и False. Каждый элемент проходит фильтрацию через булеву маску и распределяется в зависимости от значения маски:

  • Если на той же позиции в маске стоит значение True, элемент добавляется в итоговый массив
  • Если на позиции стоит значение False, то элемент не будет включен в итоговый массив

Существует три способа работы с булевой маской:

  • Создать массив значений True и False вручную
  • Использовать операторы сравнения над элементами исходного массива
  • Применить логическое отрицание к текущей маске — поменять True на False и наоборот

Так все три способа выглядят в коде:

import numpy as np
# Исходный массив
base_array = np. array([0, 1, 2, 3, 4, 5, 6, 7,])
print(base_array)
# => [0 1 2 3 4 5 6 7]
# Ручное создание маски
handmade_mask = [True, True, True, False, False, False, False, False,]
print(handmade_mask)
# => [True, True, True, False, False, False, False, False]
# Создание маски по условию
compare_mask = base_array < 3
print(compare_mask)
# => [ True  True  True False False False False False]
# Создание маски по логическому отрицанию условия
opposite_compare_mask = ~(base_array >= 3)
print(opposite_compare_mask)
# => [ True  True  True False False False False False]

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

Как применять маску

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

print(base_array[handmade_mask])
# => [0 1 2]
print(base_array[compare_mask])
# => [0 1 2]
print(base_array[opposite_compare_mask])
# => [0 1 2]

Как мы говорили выше, в Python реализована функция filter(), которая применяется для итеративной фильтрации значений списка по условию:

# Фильтрация значений с использованием filter
filtered_list = list(
    filter(
        lambda x: x < 3,
        [0, 1, 2, 3, 4, 5, 6, 7,]
    )
)
print(filtered_list)
# => [0 1 2]

Для итеративной фильтрации элементов массива numpy. ndarray используется другой способ — метод numpy.fromiter():

# Итеративное создание нового массива с использованием fromiter, тип массива задается аргументом dtype
filtered_array = np.fromiter(
    (base_array_element for base_array_element in base_array if base_array_element < 3),
    dtype = base_array.dtype
)
print(filtered_array)
# => [0 1 2]

На практике часто требуется не только убирать значения из исходного массива, но и заменять их. Для этого используется метод numpy.where():

# Заменяем отфильтрованные элементы на 0
print(np.where(base_array < 3, base_array, 0))
# => [0 1 2 0 0 0 0 0]

Если для фильтрации мы используем составное условие, лучше инициализировать маску отдельно. Это упрощает поддержку и делает код более читабельным:

# Заменяем отфильтрованные элементы на 0 с использованием маски
compare_masks = (base_array > 5) | (base_array < 3)
print(np.where(compare_masks, base_array, 0))
# => [0 1 2 0 0 0 6 7]

В реальных данных регулярно возникают пропущенные значения. Это может происходить из-за человеческого фактора, сбоя в работе сервисов или ошибки при записи в базу данных. Для таких случаев в Numpy существует отдельный тип данных numpy.nan (not a number):

# Массив с пропущенными значениями
raw_array = np.array([0, 1, None, 3, 4, None, 6, 7,], dtype=np.float64)
print(raw_array)
# => [ 0.  1. nan  3.  4. nan  6.  7.]

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

Рассмотрим на примере. Сначала нужно обнаружить пропуски:

# Маска для поиска пропусков
nan_mask = np.isnan(raw_array)
print(nan_mask)
# => [False False  True False False  True False False]

Затем чистим данные от пропусков:

# Маска для фильтрации пропущенных значений
not_nan_mask = ~nan_mask
print(raw_array[not_nan_mask])
# => [0. 1. 3. 4. 6. 7.]

Теперь заменяем пропуски на некоторое значение:

# Заменяем пропуски на 0
print(np. where(nan_mask, 0, raw_array))
# => [0. 1. 0. 3. 4. 0. 6. 7.]

Как применять маску с двумерными массивами

Выше мы рассмотрели фильтрацию значений на примере одномерного массива. Те же принципы применимы и в работе с двумерными массивами. Рассмотрим пример задачи — подготовим сырые данные продаж магазина ноутбуков по следующим шагам:

  • Сначала обнаружим выбросы двух типов — значения выше 200 и отрицательные значения. Число 200 выбрано потому, что именно столько ноутбуков хранится на складе. Менеджер магазина знает, что в день не бывает более 200 продаж
  • Затем заменим выбросы и пропуски на среднее значение продаж

Мы проводим эти операции, чтобы подготовить сырые данные к более глубокой аналитики с применением методов статистики и машинного обучения:

# Создаем список списков продаж четырех магазинов
orders_values =  [
    [7, 1, -7, None],
    [1000, 2, 4, None],
    [3, None, 503, 3],
    [8, 12, 8, 7],
    [15, 11, None, 9],
    [None, 18, 17, -21],
    [252, 16, 25, 17]
]
# Конвертируем в Numpy-массив
orders = np. array(orders_values, dtype=np.float64)
print(orders)
# => [[   7.    1.   -7.   nan]
#  [1000.    2.    4.   nan]
#  [   3.   nan  503.    3.]
#  [   8.   12.    8.    7.]
#  [  15.   11.   nan    9.]
#  [  nan   18.   17.  -21.]
#  [ 252.   16.   25.   17.]]

Чтобы отфильтровать значения, нужно создать маски:

# Маска для отрицательных значений
negative_values = orders < 0
print(orders[negative_values])
# => [ -7. -21.]
# Маска для больших значений
big_values = orders > 200
print(orders[big_values])
# => [1000.  503.  252.]
# Маска для пропущенных значений и подсчета их количества
nan_values = np.isnan(orders)
print(sum(sum(nan_values)))
# => 5

Чтобы найти средние значения, нужно оставить только стандартные значения продаж. Сделать это можно маской, объединяющей логические отрицания сформированных масок:

# Маска стандартных значений
normal_mask = ~negative_values & ~big_values & ~nan_values
# Стандартные значения заказов
normal_values = orders[normal_mask]
# Среднее значение для стандартных заказов
normal_mean = normal_values. mean()
normal_mean = int(normal_mean)
print(normal_mean)
# => 10

Далее остается только заменить нестандартные значения на величину среднего:

# Массив, в котором нестандартные значения заменены на среднее
prepared_orders = np.where(normal_mask, orders, normal_mean)
print(prepared_orders)
# => [[ 7.  1. 10. 10.]
#  [10.  2.  4. 10.]
#  [ 3. 10. 10.  3.]
#  [ 8. 12.  8.  7.]
#  [15. 11. 10.  9.]
#  [10. 18. 17. 10.]
#  [10. 16. 25. 17.]]

Выводы

Начало работы с массивами Python

Главная/Блог/Учебники и руководства/Python за 5 минут: Начало работы с массивами Python

17 сентября 2021 г. — чтение 5 мин. языки. Он используется во многих различных областях, таких как веб-разработка, наука о данных, машинное обучение и многое другое. Массивы — важная структура данных в Python. Массивы позволяют нам хранить несколько значений одновременно и выполнять действия над этими значениями. В этом руководстве по Python мы рассмотрим массивы Python, операции с массивами и многое другое.

Мы рассмотрим :

  • Что такое массивы Python?
  • Создать массив Python
  • Общие операции с массивами
  • 2D-массивы Python
  • Подведение итогов и следующие шаги


Что такое массивы Python?

Массив Python — это контейнер, который может содержать несколько элементов одного типа данных в одной переменной. Это позволяет нам хранить несколько элементов одного типа вместе. Массивы очень распространены. Большинство структур данных используют массивы для выполнения алгоритмов, а массивы очень часто используются в таких областях, как наука о данных и машинное обучение. Мы можем использовать массивы, когда хотим манипулировать данными определенного типа. Они очень полезны при работе с большими объемами данных, поскольку массивы могут содержать большое количество элементов.

Элемент массива — это элемент, хранящийся в массиве. Таким образом, элементы «Спот», «Макс» и «Сэм» из нашего массива собак являются элементами. Индекс относится к местоположению числового индекса каждого элемента. Мы можем использовать индекс для идентификации и доступа к элементам.

Что такое списки Python?

Хотя иногда их путают, список Python отличается от массива Python. Список — это набор элементов, который содержит данные разных типов. Это означает, что первый элемент списка может быть строкой, второй элемент может быть целым числом, а третий может быть списком строк и т. д. Списки упорядочены, изменяемы, и их элементы не обязательно должны быть уникальными. .

Мы можем преобразовать наши списки Python в массивы с помощью библиотеки NumPy Python, используя одну из следующих функций массива NumPy:

  • numpy.массив ()
  • numpy.asarray()

Создать массив Python

Мы можем создать новый массив с помощью модуля массива и команды import array . Давайте посмотрим на пример программы Python:

 

собаки = ["Спот", "Макс", "Сэм", "Чарли", "Купер", "Герцог", "Медведь", "Бадди", "Майло". ", "Мерфи"]

print(dogs)

Теперь давайте посмотрим на различные операции, которые мы можем выполнять с массивами Python.

Общие операции с массивами

Добавление элементов

Существует несколько способов добавления элементов в массив. Мы можем добавить элементы в конец массива, используя метод append() :

 

собаки = ["Спот", "Макс", "Сэм", "Чарли", "Купер", "Герцог", " Медведь", "Бадди", "Майло", "Мерфи"]

dogs.append("Ригли")

print(dogs)

Мы можем использовать метод insert() для добавления элемента в указанное место индекса в нашем массиве. Вот пример:

 

собаки = ["Спот", "Макс", "Сэм", "Чарли", "Купер", "Герцог", "Медведь", "Бадди", "Майло", "Мерфи"]

dogs.insert(1, "Wrigley")

print(dogs)

Элементы доступа

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

 

собаки = ["Спот", "Макс", "Сэм", "Чарли", "Купер", "Дюк", "Медведь", "Бадди", "Майло", "Мерфи"]

x = dogs[0]

print(x)

Если мы хотим получить доступ к определенному элементу и изменить этот элемент, мы получаем доступ к номеру индекса и устанавливаем его равным измененному значению. Например:

 

собаки = ["Спот", "Макс", "Сэм", "Чарли", "Купер", "Дюк", "Медведь", "Бадди", "Майло", "Мерфи"]

собаки[0] = "Джек"

print(dogs)

Удаление элементов

Существует несколько способов удаления элементов массива. Мы можем использовать функцию Python pop() для удаления элемента в указанной позиции. Допустим, мы хотим удалить последний элемент нашего массива собак :

 

собак = ["Спот", "Макс", "Сэм", "Чарли", "Купер", "Герцог", "Медведь", "Buddy", "Milo", "Murphy"]

dogs.pop(9)

print(dogs)

Мы можем использовать remove() метод для удаления указанного элемента из массива. Вот пример:

 

собаки = ["Спот", "Макс", "Сэм", "Чарли", "Купер", "Герцог", "Медведь", "Бадди", "Майло", "Мерфи"]

dogs.remove("Duke")

print(dogs)

Найти длину массива

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

 

собаки = ["Спот", "Макс", "Сэм", "Чарли", "Купер", " Герцог", "Медведь", "Бадди", "Майло", "Мерфи"]

x = len(dogs)

print(x)


Продолжайте изучать Python бесплатно.

Начните бесплатно изучать основы Python с помощью нашей 1-недельной образовательной неограниченной пробной версии. Текстовые схемы обучения Educative легко просматриваются и включают живую среду кодирования, что делает обучение быстрым и эффективным.

Python для программистов


Сортировка массива Python

Мы можем использовать метод sort() для сортировки нашего массива в порядке возрастания или убывания. Если мы хотим отсортировать наш массив в порядке возрастания, мы можем использовать следующий код:

 

числа = [5, 13, 25, 2, 98, 56, 4, 8]

числа.sort()

print(числа)

следующий код:

 

числа = [5, 13, 25, 2, 98, 56, 4, 8]

числа. sort(reverse=True)

print(числа)

Подсчет элементов

9 0002 Мы можем используйте метод count() , чтобы вернуть количество элементов с указанным значением. Например, предположим, что мы хотим вернуть количество раз, когда значение «Spot» появляется в наших собак массив:

 

собаки = ["Спот", "Макс", "Сэм", "Чарли", "Купер", "Дюк", "Медведь", "Бадди", "Майло", "Мерфи" ]

x = dogs.count("Spot")

print(x)

Двумерные массивы Python

Двумерный массив или двумерный массив — это когда в массиве есть массив. В стандартном массиве каждый элемент имеет индекс. В двумерном массиве каждый элемент имеет два индекса. Давайте перейдем к примеру. Представьте, что нам нужно отслеживать, как меняется температура в течение дня. Мы будем измерять четыре температуры: одну рано утром, одну поздно утром, одну днем ​​и одну вечером. Мы можем хранить эти температуры в двумерном массиве.

 

температуры = [[52, 60, 66, 63], [50, 58, 62, 60], [53, 61, 67,64], [51, 59, 65, 62]]

We может выполнять те же действия с 2D-массивами, что и со стандартными массивами.

Подведение итогов и следующие шаги

Поздравляем с первыми шагами в работе с массивами Python! Массивы являются важной и распространенной структурой данных. Они имеют широкий спектр применения и обычно используются для выполнения алгоритмов. Нам еще многое предстоит узнать о языке программирования Python. Некоторые рекомендуемые концепции, которые следует рассмотреть далее, включают:

  • Словари Python
  • кортежей Python
  • Строки Python
  • Синтаксис Python

Чтобы начать изучение этих и других концепций, ознакомьтесь с планом обучения Educative Python для программистов . На этом пути вы начнете с изучения основ Python, а затем перейдете к более сложным концепциям, включая модули и веб-задачи. К концу у вас будут продвинутые знания, чтобы уверенно использовать Python в вашем следующем проекте.

Приятного обучения!

Продолжить изучение Python

  • Анализ данных стал проще: учебник по Python Pandas
  • Рекомендации по обращению строки в JavaScript, C++ и Python
  • 50 вопросов и ответов на собеседовании по Python

НАПИСАН BYErin Schaffer

Работа с массивами Python — Документация по Cython 3.

0.0rc1.dev0

Примечание

На этой странице используются два разных варианта синтаксиса:

  • Специфический для Cython синтаксис cdef , который был разработан для объявления типов кратким и легко читаемым с точки зрения C/C++.

  • Чистый синтаксис Python, который позволяет статические объявления типов Cython в чистый код Python, следующие подсказки типа PEP-484 и переменные аннотации PEP 526.

    Чтобы использовать типы данных C в синтаксисе Python, вам необходимо импортировать специальный модуль cython в модуле Python, который вы хотите скомпилировать, например.

     импортный цитон
     

    Если вы используете чистый синтаксис Python, мы настоятельно рекомендуем использовать недавнюю Релиз Cython 3, так как здесь были внесены значительные улучшения по сравнению с версиями 0.29.x.

Python имеет встроенный модуль массива, поддерживающий динамические одномерные массивы примитивные виды. Можно получить доступ к базовому массиву C Python массив из Cython. В то же время это обычные объекты Python. которые можно хранить в списках и сериализовать между процессами при использовании многопроцессорность .

По сравнению с ручным подходом с malloc() и free() , этот дает безопасное и автоматическое управление памятью Python, и по сравнению с В массиве Numpy нет необходимости устанавливать зависимость, так как массив Модуль встроен как в Python, так и в Cython.

Безопасное использование с представлениями памяти

 из массива импорта cython.cimports.cpython
массив импорта
a = cython.declare(массив.массив, массив.массив('i', [1, 2, 3]))
ca = cython.declare(cython.int[:], а)
печать (ca [0])
 

NB: импорт переносит обычный объект массива Python в пространство имен. в то время как cimport добавляет функции, доступные из Cython.

Массив Python создается с помощью сигнатуры типа и последовательности начальные значения. Возможные сигнатуры типов см. в Python документация для модуля массива.

Обратите внимание, что когда массив Python присваивается переменной с типом представление памяти, будут небольшие накладные расходы на построение памяти вид. Однако с этого момента переменная может быть передана другим работает без накладных расходов, пока он набирается:

 из массива импорта cython.cimports.cpython
массив импорта
a = cython.declare(массив.массив, массив.массив('i', [1, 2, 3]))
ca = cython.declare(cython.int[:], а)
@cython.cfunc
def overhead(a: cython.object) -> cython.int:
    ca: cython.int[:] = а
    возврат ca[0]
@cython.cfunc
def no_overhead(ca: cython.int[:]) -> cython.int:
    возврат ca[0]
print(overhead(a)) # будет построено новое представление памяти, служебные данные
print(no_overhead(ca)) # ca уже является представлением памяти, так что никаких накладных расходов
 

Небезопасный доступ к необработанному указателю C с нулевыми издержками

Чтобы избежать накладных расходов и иметь возможность передавать указатель C на другие функций, можно получить доступ к базовому непрерывному массиву как указатель. Проверка типов и границ не предусмотрена, поэтому будьте осторожны при использовании правильный тип и подпись.

 из массива импорта cython.cimports.cpython
массив импорта
a = cython.declare(массив.массив, массив.массив('i', [1, 2, 3]))
# доступ к основному указателю:
печать (a.data.as_ints [0])
из cython.cimports.libc.string импортировать memset
набор памяти (a.data.as_voidptr, 0, длина (a) * cython.sizeof (cython.int))
 

Обратите внимание, что любая операция по изменению длины объекта массива может сделать недействительным указатель.

Клонирование, расширение массивов

Чтобы не использовать конструктор массива из модуля Python, можно создать новый массив того же типа, что и шаблон, и предварительно выделить заданное количество элементов. Массив инициализируется ноль по запросу.

 из массива импорта cython.cimports.cpython
массив импорта
int_array_template = cython.declare(массив.массив, массив.массив('i', []))
cython.declare(newarray=массив. массив)
# создать массив из 3 элементов того же типа, что и шаблон
newarray = array.clone (int_array_template, 3, ноль = ложь)
 

Массив также можно расширять и изменять его размер; это позволяет избежать повторного запоминания перераспределение, которое произойдет, если элементы будут добавлены или удалены по одному.

 из массива импорта cython.cimports.cpython
массив импорта
a = cython.declare(массив.массив, массив.массив('i', [1, 2, 3]))
b = cython.declare(массив.массив, массив.массив('i', [4, 5, 6]))
# расширить a с помощью b, изменить размер по мере необходимости
массив.расширить(а, б)
# изменить размер a, оставив только три исходных элемента
array.resize (а, длина (а) - длина (б))
 

Ссылка API

Поля данных

 данные.as_voidptr
data.as_chars
data.as_schars
data.as_uchars
data.as_shorts
data.as_ushorts
data.as_ints
data.as_uints
data.as_longs
data.as_ulongs
data.as_longlongs # требуется Python >=3
data.as_ulonglongs # требуется Python >=3
data. as_floats
data.as_doubles
data.as_pyunicodes
 

Прямой доступ к базовому непрерывному массиву C с заданным типом; например, myarray.data.as_ints .

Функции

Следующие функции доступны для Cython из модуля массива

 @cython.cfunc
@cython.exceptval(-1)
def resize(self: array.array, n: cython.Py_ssize_t) -> cython.int
 

Быстрое изменение размера/перераспределение. Не подходит для повторяющихся небольших приращений; изменяет размеры базового массива точно до запрошенной суммы.


 @cython.cfunc
@cython.exceptval(-1)
def resize_smart(self: array.array, n: cython.Py_ssize_t) -> cython.int
 

Эффективен для небольших приращений; использует модель роста, которая обеспечивает амортизированные линейные добавления.


 @cython.cfunc
@cython.inline
def clone (шаблон: array.array, длина: cython.Py_ssize_t, ноль: cython.bint) -> array.array
 

Быстрое создание нового массива по шаблонному массиву.