NumPy 2.x: повний посібник з міграції та нових можливостей (від 2.0 до 2.4)

Повний посібник з NumPy 2.x — від 2.0 до 2.4. NEP 50, StringDType, Array API, прискорення до 15x, free-threaded Python та покрокова міграція з 1.x із практичними прикладами коду.

Вступ

NumPy 2.x — це, мабуть, найбільш значуще оновлення фундаментальної бібліотеки для наукових обчислень у Python за останні роки. Перший реліз NumPy 2.0 вийшов у червні 2024 року, завершивши майже п'ятирічний цикл розробки. І, чесно кажучи, зміни вийшли настільки масштабними, що ландшафт Python-екосистеми для data science вже ніколи не буде таким, як раніше. Від початкового NumPy 2.0 до найновішого NumPy 2.4 (грудень 2025) бібліотека пройшла серйозну еволюцію — десятки нових функцій, покращення продуктивності та купа виправлень.

Чому NumPy 2.x настільки важливий?

По-перше, це перший major реліз з порушенням зворотної сумісності за понад 15 років. По-друге, він закладає фундамент для майбутнього розвитку всієї наукової екосистеми Python — включаючи pandas 3.0, scikit-learn та інші критичні пакети. І, нарешті, NumPy 2.x приносить суттєві покращення продуктивності — від 6x прискорення скалярних обчислень до 15x прискорення операцій з рядками. Так, ви не помилилися — до п'ятнадцяти разів швидше.

У цій статті ми детально розглянемо ключові зміни в кожній версії від 2.0 до 2.4, покажемо практичні приклади міграції коду, проаналізуємо вплив на продуктивність і подивимося, як все це працює разом із pandas 3.0, Polars та scikit-learn 1.8. Отже, поїхали.

Ключові зміни в NumPy 2.0

NEP 50: нові правила просування типів

Одна з найважливіших змін у NumPy 2.0 — впровадження NEP 50 (NumPy Enhancement Proposal 50). Ця пропозиція радикально переробила правила просування типів при операціях між масивами та скалярами. У старому NumPy 1.x Python-скаляри могли змінювати тип масиву абсолютно непередбачуваним чином, що призводило до втрати точності та, відверто кажучи, дуже дивної поведінки.

# NumPy 1.x — проблемна поведінка
import numpy as np

arr = np.array([1, 2, 3], dtype=np.int8)
result = arr + 200  # Переповнення! Результат: [-56, -54, -52]

# NumPy 2.x — безпечна поведінка
arr = np.array([1, 2, 3], dtype=np.int8)
result = arr + 200  # Автоматичне просування до int64
# Результат: [201, 202, 203]

Нові правила працюють так: коли ви виконуєте операцію між масивом та Python-скаляром, NumPy 2.x розглядає скаляр як «слабкий тип» і автоматично просуває його до типу масиву, якщо це можливо без втрати даних. Якщо ж просування небезпечне (як у прикладі вище з переповненням), бібліотека використовує безпечніший тип. Логічно, правда?

# Приклад з float32
arr_float32 = np.array([1.5, 2.5, 3.5], dtype=np.float32)

# NumPy 1.x: результат був би float64
# NumPy 2.x: результат залишається float32
result = arr_float32 * 2.0  # dtype=float32
print(result.dtype)  # float32

# Але якщо операція вимагає вищої точності:
arr_small = np.array([1, 2, 3], dtype=np.int8)
result = arr_small * 256  # Автоматично просувається до int16/int32
print(result.dtype)  # int16 або int32 (залежно від платформи)

Злам ABI: що це означає для екосистеми

NumPy 2.0 зламав Application Binary Interface (ABI). Якщо ви не знайомі з цим терміном — це означає, що пакети, скомпільовані з NumPy 1.x, несумісні на бінарному рівні з NumPy 2.x. Чесно кажучи, це була найбільш болюча зміна для екосистеми, адже вимагала перекомпіляції всіх залежних пакетів: pandas, scikit-learn, scipy, matplotlib та багатьох інших.

Але команда NumPy вжила заходів для пом'якшення цього переходу. Починаючи з NumPy 1.26, була введена шар сумісності ABI, яка дозволяє пакетам, скомпільованим з NumPy 1.x, працювати з NumPy 2.x на рівні API (хоча й з деякими обмеженнями). Це дало екосистемі час на адаптацію — і більшість популярних пакетів вже мігрували.

Зміна цілочисельного типу за замовчуванням на Windows

На платформах Windows NumPy 2.0 змінив цілочисельний тип за замовчуванням з int32 на int64, приводячи поведінку у відповідність з Linux та macOS. Ця зміна може здатися незначною, але вона усуває величезну кількість проблем з переносимістю коду між платформами.

# Перевірка типу за замовчуванням
arr = np.array([1, 2, 3])
print(arr.dtype)

# NumPy 1.x на Windows: int32
# NumPy 2.x на Windows: int64
# NumPy 1.x/2.x на Linux/macOS: int64 (завжди)

# Це впливає на індексацію та операції:
index = np.array([0, 1, 2])  # Тепер int64 на всіх платформах

Новий StringDType та простір імен numpy.strings

А ось ця зміна — одна з моїх улюблених. NumPy 2.0 представив експериментальний StringDType — новий тип даних змінної довжини для роботи з рядками UTF-8, який значно ефективніший за старий object dtype. Разом із ним з'явився новий простір імен numpy.strings з продуктивними ufunc-функціями для обробки рядків.

# Використання StringDType
arr_strings = np.array(['hello', 'world', 'numpy'], dtype=np.dtypes.StringDType())

# Операції з рядками через numpy.strings
result_upper = np.strings.upper(arr_strings)
print(result_upper)  # ['HELLO', 'WORLD', 'NUMPY']

# Пошук підрядка
contains = np.strings.find(arr_strings, 'o')
print(contains)  # [4, 1, -1] (індекси першого входження або -1)

# Заміна підрядків
replaced = np.strings.replace(arr_strings, 'o', '0')
print(replaced)  # ['hell0', 'w0rld', 'numpy']

Підтримка Array API стандарту

NumPy 2.0 також впроваджує підтримку Array API стандарту — ініціативи, яка спрямована на уніфікацію API між різними бібліотеками масивів (NumPy, CuPy, JAX, PyTorch). По суті, ви пишете код один раз і він працює з будь-яким бекендом.

# np.isdtype — перевірка типу даних
print(np.isdtype(np.float32, 'real floating'))  # True
print(np.isdtype(np.int64, 'integral'))  # True

# np.vecdot — векторний скалярний добуток
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
result = np.vecdot(a, b)
print(result)  # [17, 53]

Нові функції лінійної алгебри

NumPy 2.0 додав кілька нових функцій для лінійної алгебри, які роблять код більш виразним та ефективним. Якщо ви часто працюєте з матричними обчисленнями — оціните.

# matrix_norm — норма матриці
matrix = np.array([[1, 2], [3, 4]])
frobenius_norm = np.linalg.matrix_norm(matrix, 'fro')
print(frobenius_norm)  # 5.477225575051661

# vector_norm — норма вектора
vector = np.array([3, 4])
l2_norm = np.linalg.vector_norm(vector)
print(l2_norm)  # 5.0

# svdvals — лише сингулярні значення (швидше за повний SVD)
singular_values = np.linalg.svdvals(matrix)
print(singular_values)  # [5.4649857  0.36596619]

Покращення var/std та нові unique_* функції

Функції обчислення дисперсії та стандартного відхилення отримали новий параметр correction замість старого ddof, а також параметр mean для використання попередньо обчисленого середнього. Крім того, функціональність np.unique() була розділена на спеціалізовані функції — і це робить код набагато чистішим.

# Новий параметр correction (замість ddof)
data = np.array([1, 2, 3, 4, 5])
variance = np.var(data, correction=1)
print(variance)  # 2.5

# Використання попередньо обчисленого середнього
mean_value = np.mean(data)
variance_fast = np.var(data, mean=mean_value)

# Нові unique_* функції
data = np.array([1, 2, 2, 3, 3, 3, 4])

# unique_counts — лише значення та їх кількість
values, counts = np.unique_counts(data)
print(values)  # [1, 2, 3, 4]
print(counts)  # [1, 2, 3, 1]

# unique_inverse — для реконструкції
values, inverse = np.unique_inverse(data)
reconstructed = values[inverse]
print(np.array_equal(data, reconstructed))  # True

# unique_values — лише унікальні значення (найшвидша)
values = np.unique_values(data)

Властивість .mT для транспонування матриць

Нова властивість .mT дозволяє транспонувати останні два виміри багатовимірного масиву. Це особливо зручно при роботі з батчами матриць — більше не потрібно вручну переставляти осі.

# Транспонування матриць у батчі
batch_matrices = np.random.rand(10, 3, 4)  # 10 матриць 3x4

# Нова версія (простіше та зрозуміліше):
transposed_new = batch_matrices.mT
print(transposed_new.shape)  # (10, 4, 3)

# Для 3D+ масивів .mT та .T відрізняються:
arr_3d = np.random.rand(2, 3, 4)
print(arr_3d.T.shape)    # (4, 3, 2) — повне транспонування
print(arr_3d.mT.shape)   # (2, 4, 3) — транспонування лише матриць

Видалені функції та їх заміни

NumPy 2.0 видалив чимало застарілих функцій та API. Ось таблиця найважливіших змін, яку варто тримати під рукою при міграції:

Видалено в NumPy 2.0 Заміна Примітка
np.bool, np.int, np.float np.bool_, np.int_, np.float64 Уникнення конфлікту з вбудованими типами Python
np.product() np.prod() Стандартизація назви
np.cumproduct() np.cumprod() Стандартизація назви
np.sometrue(), np.alltrue() np.any(), np.all() Більш зрозумілі назви
np.find_common_type() np.result_type() Надійніше просування типів
np.in1d() np.isin() Зрозуміліша назва
np.trapz() np.trapezoid() Повна назва замість абревіатури
np.Inf, np.NaN np.inf, np.nan Дотримання стандартів PEP 8

Еволюція NumPy 2.1–2.3

NumPy 2.1: покращення продуктивності

NumPy 2.1 вийшов у серпні 2024 і був зосереджений на покращенні продуктивності та стабільності після великого релізу 2.0. Серед основних покращень — оптимізація операцій з пам'яттю, швидші обчислення для малих масивів та покращена підтримка SIMD інструкцій на різних платформах.

# Покращення продуктивності для малих масивів у 2.1
small_arrays = [np.random.rand(10) for _ in range(1000)]

# Операції з малими масивами стали швидшими на 20-30%
result = [arr.sum() for arr in small_arrays]

# Оптимізована робота з view та копіями
large_array = np.random.rand(1000000)
view = large_array[::2]  # View операція оптимізована
copy = large_array[::2].copy()  # Copy також швидша

NumPy 2.2: array representation та __array_wrap__

NumPy 2.2 (грудень 2024) покращив текстове представлення масивів та переробив механізм __array_wrap__. Якщо ви коли-небудь писали власні підкласи ndarray — ви знаєте, наскільки це важливо для правильної інтеграції.

# Покращене відображення великих масивів
large_array = np.random.rand(100, 100)
print(large_array)
# Тепер показує більш компактне та інформативне представлення

# Покращена підтримка __array_wrap__ для підкласів
class MyArray(np.ndarray):
    def __new__(cls, input_array):
        obj = np.asarray(input_array).view(cls)
        return obj

    def __array_wrap__(self, result, context=None):
        return np.ndarray.__array_wrap__(self, result, context)

my_arr = MyArray([1, 2, 3])
result = np.sqrt(my_arr)  # Зберігає тип MyArray

NumPy 2.3: free-threaded Python та анотації

NumPy 2.3 (червень 2025) — це реліз, який готує ґрунт для справжньої революції в Python. Він додав покращену підтримку free-threaded Python (PEP 703), покращив type annotations для кращої інтеграції з type checkers та очистив застарілий код.

# Підтримка free-threaded Python (експериментально)
# Дозволяє справжню паралелізацію без GIL
import numpy as np
from concurrent.futures import ThreadPoolExecutor

def compute_intensive(arr):
    return np.linalg.svd(arr)

# З free-threaded Python це може працювати паралельно
matrices = [np.random.rand(100, 100) for _ in range(10)]
with ThreadPoolExecutor() as executor:
    results = list(executor.map(compute_intensive, matrices))

NumPy 2.4: останні нововведення

Нове приведення 'same_value'

NumPy 2.4 (грудень 2025) представив новий режим приведення типів 'same_value', який гарантує, що значення залишиться незмінним при конвертації типів. Це корисна штука, коли вам потрібна впевненість, що конвертація не зіпсує дані.

# Режим приведення 'same_value'
arr = np.array([1.0, 2.0, 3.0])

# Безпечне приведення до int (значення зберігаються)
int_arr = arr.astype(np.int64, casting='same_value')
print(int_arr)  # [1, 2, 3]

# Це викличе помилку, якщо значення зміниться:
arr_with_fraction = np.array([1.5, 2.7, 3.9])
try:
    int_arr = arr_with_fraction.astype(np.int64, casting='same_value')
except TypeError as e:
    print(f"Помилка: {e}")  # Неможливо безпечно привести

Параметр ndmax для np.array()

Новий параметр ndmax дозволяє контролювати максимальну глибину рекурсії при створенні масиву з вкладених послідовностей. Здається простим, але на практиці це може врятувати від неочікуваних багів при роботі зі складними структурами даних.

# Контроль вимірності за допомогою ndmax
data = [[1, 2], [3, 4]]

# За замовчуванням: створює 2D масив
a = np.array(data, dtype=object)
print(a.shape)  # (2, 2)

# З ndmax=1: створює 1D масив списків
b = np.array(data, dtype=object, ndmax=1)
print(b.shape)  # (2,)

Покращення StringDType та MaskedArray

StringDType продовжує еволюціонувати. Тепер він повністю підтримує fill_value при роботі з masked arrays, включаючи збереження через зрізи та views. Це робить його ще зручнішим для реальних завдань обробки даних.

# Використання StringDType з MaskedArray
import numpy.ma as ma

data = np.array(['apple', 'banana', '', 'cherry'],
                dtype=np.dtypes.StringDType())
masked_data = ma.masked_array(data, mask=[False, False, True, False])

# Встановлення fill_value для пропущених значень
masked_data.fill_value = 'MISSING'
print(masked_data.filled())  # ['apple', 'banana', 'MISSING', 'cherry']

6x прискорення скалярних ufunc та 15x для unique()

Ось де стає по-справжньому цікаво. NumPy 2.4 приніс драматичне прискорення для universal functions (ufunc) при роботі зі скалярами — до 6 разів швидше. Різниця швидкості між math.sin та np.sin для скалярів зменшилася з 19x до лише 3x. А np.unique() для рядків стала до 15 разів швидшою завдяки hash-базованому алгоритму видалення дублікатів.

# Бенчмарк np.unique() для рядків
import time

string_data = np.array(['apple', 'banana', 'apple', 'cherry', 'banana'] * 10000,
                       dtype=np.dtypes.StringDType())

start = time.perf_counter()
unique_strings = np.unique(string_data)
end = time.perf_counter()
print(f"Час виконання: {(end - start) * 1000:.2f} мс")

# NumPy 2.4 з StringDType: ~5 мс
# NumPy 1.x з object dtype: ~75 мс
# Прискорення: ~15x

Протокол __numpy_dtype__ та інші нововведення

NumPy 2.4 також представив протокол __numpy_dtype__, який дозволяє користувацьким об'єктам визначати конвертацію в NumPy dtype через np.dtype(obj). Крім того, np.size() тепер підтримує кортеж осей, np.ndindex() став у 5.2 рази швидшим (завдяки використанню itertools.product), а понад 300 функцій отримали підтримку inspect.signature().

# Протокол __numpy_dtype__
class MyCustomType:
    def __numpy_dtype__(self):
        return np.dtype('float32')

# Використання з np.dtype()
custom = MyCustomType()
dt = np.dtype(custom)
print(dt)  # float32

# np.size() з кількома осями
arr = np.random.rand(4, 5, 6)
size_multiple = np.size(arr, axis=(0, 1))  # 4 * 5 = 20
print(size_multiple)  # 20

Застарілі та видалені функції в 2.4

NumPy 2.4 продовжив очищення API — процес, який команда розпочала ще у версії 2.0. Серед deprecated: встановлення ndarray.strides напряму (використовуйте stride_tricks.as_strided()), функція np.fix() (замінена на np.trunc()), модифікація ndarray.shape на місці (використовуйте np.reshape()). Серед видалених: np.in1d(), np.trapz() і MachAr для обчислення машинних лімітів.

Практичний посібник з міграції

Покроковий план міграції з NumPy 1.x

Міграція на NumPy 2.x вимагає систематичного підходу. Я пройшов через це на кількох проєктах і можу сказати — якщо дотримуватися плану, процес проходить набагато менш болісно, ніж здається на перший погляд.

Крок 1: Оцінка готовності залежностей

# Перевірте версії ваших залежностей
import importlib.metadata

critical_packages = {
    'pandas': '2.1.0',
    'scikit-learn': '1.3.0',
    'scipy': '1.11.0',
    'matplotlib': '3.7.0',
}

for package, min_ver in critical_packages.items():
    try:
        version = importlib.metadata.version(package)
        status = "OK" if version >= min_ver else f"ОНОВИТИ до {min_ver}+"
        print(f"{package}: {version} — {status}")
    except importlib.metadata.PackageNotFoundError:
        print(f"{package}: не встановлено")

Крок 2: Автоматичний аудит коду з ruff

# Встановіть ruff та запустіть перевірку NPY201
# pip install ruff
# ruff check --select NPY201 .

# Автоматичне виправлення:
# ruff check --select NPY201 --fix .

# Це автоматично замінить:
# np.bool → np.bool_
# np.float → np.float64
# np.product() → np.prod()
# np.sometrue() → np.any()
# np.alltrue() → np.all()

Крок 3: Виправлення типових помилок

# Помилка 1: AttributeError з видаленими типами
# Стара версія (помилка):
# dtype = np.bool
# Виправлення:
dtype = np.bool_

# Помилка 2: Зміни в просуванні типів
arr_int8 = np.array([100, 120, 127], dtype=np.int8)
# NumPy 1.x: arr_int8 + 200 → переповнення
# NumPy 2.x: arr_int8 + 200 → автоматичне просування до int64

# Помилка 3: np.gradient тепер повертає tuple замість list
result = np.gradient(np.array([1.0, 4.0, 9.0, 16.0]))
print(type(result))  # numpy.ndarray (для 1D)
# Для N-D масивів: тепер tuple замість list

# Помилка 4: np.unique() з рядками може повертати несортовані дані
# У NumPy 2.4 hash-based алгоритм не гарантує сортування

Крок 4: Тестування та валідація

# Скрипт для комплексної перевірки сумісності
import numpy as np
import warnings

def validate_numpy_migration():
    issues = []

    # Перевірка версії
    version = tuple(int(x) for x in np.__version__.split('.')[:2])
    if version < (2, 0):
        issues.append(f"NumPy {np.__version__} — потрібна версія 2.0+")

    # Перевірка правил просування типів
    arr = np.array([1], dtype=np.int8)
    result = arr + np.float64(1.0)
    if result.dtype != np.float64:
        issues.append("Несподіване просування типів")

    # Перевірка StringDType
    try:
        s = np.array(['test'], dtype=np.dtypes.StringDType())
        issues_str = False
    except Exception:
        issues.append("StringDType не підтримується")

    if issues:
        print("Проблеми міграції:")
        for issue in issues:
            print(f"  - {issue}")
    else:
        print("Міграція успішна! Усі перевірки пройдені.")

validate_numpy_migration()

Продуктивність та оптимізація

Порівняння продуктивності NumPy 1.x vs 2.x

Давайте подивимося на реальні цифри. Нижче — детальне порівняння продуктивності між версіями NumPy на типових операціях:

# Бенчмарк-сюїта для типових операцій
import timeit
import numpy as np

def benchmark(stmt, setup='', number=1000):
    t = timeit.timeit(stmt, setup=setup, globals=globals(), number=number)
    return t / number * 1000  # мілісекунди

# 1. Скалярні ufunc (найбільше прискорення в 2.4)
arr = np.random.rand(1_000_000)
t = benchmark('np.sin(arr) + 2.0')
print(f"Скалярні ufunc: {t:.3f} мс")
# NumPy 1.x: ~12 мс → NumPy 2.4: ~2 мс (6x)

# 2. np.unique() для рядків
strings = np.array(['item_' + str(i % 100) for i in range(100_000)],
                   dtype=np.dtypes.StringDType())
t = benchmark('np.unique(strings)', number=100)
print(f"unique() рядки: {t:.3f} мс")
# NumPy 1.x (object): ~80 мс → NumPy 2.4: ~5 мс (15x)

# 3. np.ndindex() — 5.2x прискорення
t = benchmark('list(np.ndindex(10, 10, 10))', number=100)
print(f"ndindex: {t:.3f} мс")

Поради для максимальної продуктивності

Ось кілька практичних порад, які допоможуть витиснути максимум з NumPy 2.x:

# Порада 1: Використовуйте StringDType замість object
# Повільно (object dtype):
slow = np.array(['apple'] * 10000, dtype=object)

# Швидко (StringDType):
fast = np.array(['apple'] * 10000, dtype=np.dtypes.StringDType())

# Порада 2: Використовуйте спеціалізовані unique_* функції
data = np.random.randint(0, 100, 100_000)

# Якщо потрібні лише значення: unique_values (найшвидша)
values = np.unique_values(data)

# Якщо потрібні значення та кількість: unique_counts
values, counts = np.unique_counts(data)

# Порада 3: svdvals замість повного SVD
matrix = np.random.rand(500, 500)
# Повільно: U, s, Vh = np.linalg.svd(matrix)
# Швидко:
s = np.linalg.svdvals(matrix)

# Порада 4: Параметр mean у np.var()
mean = np.mean(data.astype(float))
var = np.var(data.astype(float), mean=mean)  # Не обчислює mean повторно

Сумісність з екосистемою

Інтеграція з pandas 3.0, scikit-learn та Polars

NumPy 2.x — це фундамент всієї екосистеми наукового Python. Давайте подивимося, як він працює разом із найпопулярнішими інструментами:

# NumPy 2.x + pandas 3.0
import pandas as pd
import numpy as np

# pandas 3.0 повністю сумісний з NumPy 2.x
df = pd.DataFrame({
    'values': np.random.rand(1000),
    'categories': np.array(['A', 'B', 'C'] * 333 + ['A'],
                          dtype=np.dtypes.StringDType())
})

# NumPy 2.x + scikit-learn
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression

X = np.random.rand(100, 5)
y = np.random.rand(100)

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)  # NumPy 2.x під капотом

model = LinearRegression()
model.fit(X_scaled, y)
print(model.predict(X_scaled).dtype)  # float64

# NumPy 2.x + Polars
import polars as pl

numpy_data = np.random.rand(1000, 3)
df_polars = pl.DataFrame({
    'col1': numpy_data[:, 0],
    'col2': numpy_data[:, 1],
    'col3': numpy_data[:, 2]
})

# Polars → NumPy (використовує NumPy 2.x API)
back_to_numpy = df_polars.to_numpy()
print(type(back_to_numpy))  # numpy.ndarray

Free-threaded Python: перспективи

NumPy 2.3+ впроваджує підтримку free-threaded Python (PEP 703), і це, на мою думку, одна з найперспективніших змін. Справжня паралелізація без Global Interpreter Lock може стати революційною зміною для обчислювально інтенсивних завдань:

# Free-threaded NumPy (Python 3.13t+)
from concurrent.futures import ThreadPoolExecutor
import numpy as np

def matrix_operation(matrix):
    """Обчислювально інтенсивна операція"""
    return np.linalg.svd(matrix, full_matrices=False)

matrices = [np.random.rand(500, 500) for _ in range(8)]

# Без GIL — справжня паралелізація на 8 ядрах
with ThreadPoolExecutor(max_workers=8) as executor:
    results = list(executor.map(matrix_operation, matrices))

# Перевірка статусу free-threading:
import sys
free_threaded = hasattr(sys.flags, 'gil') and sys.flags.gil == 0
print(f"Free-threaded: {free_threaded}")

Порівняння версій NumPy 2.x

Щоб було легше орієнтуватися, ось зведена таблиця ключових нововведень у кожній версії:

Функція NumPy 2.0 NumPy 2.1 NumPy 2.2 NumPy 2.3 NumPy 2.4
NEP 50 (просування типів) Впроваджено
StringDType Експериментально Покращення Покращення Покращення MaskedArray fill_value
Array API стандарт Впроваджено Розширення Розширення Розширення Розширення
Free-threaded Python Початкова підтримка Покращена підтримка
Прискорення скалярних ufunc 6x швидше
unique() для рядків 15x швидше
Приведення 'same_value' Додано
ndmax параметр Додано
__numpy_dtype__ протокол Додано
Signature introspection 300+ функцій

Висновки та рекомендації

NumPy 2.x — це не просто чергове оновлення бібліотеки, а фундаментальна зміна в екосистемі наукового Python. Від NEP 50, що переробила правила просування типів, до 15-кратного прискорення операцій з рядками у версії 2.4 — кожен реліз приносить щось суттєве.

Мігруйте зараз, якщо:

  • Ви починаєте новий проєкт і хочете використовувати сучасний API
  • Ви інтенсивно працюєте з рядковими даними — StringDType дає величезний виграш продуктивності
  • Вам потрібна максимальна швидкість обчислень — прискорення до 6x-15x
  • Ви вже використовуєте pandas 3.0 або scikit-learn 1.5+ — вони оптимізовані для NumPy 2.x

Почекайте з міграцією, якщо:

  • У вас великий legacy codebase з критичними залежностями, які ще не підтримують NumPy 2.x
  • Ваші процеси залежать від специфічної поведінки NumPy 1.x (наприклад, старі правила просування типів)
  • У вас немає ресурсів на тестування та виправлення проблем сумісності

Використовуйте ruff --select NPY201 --fix для автоматичного виправлення більшості проблем сумісності. Починайте з тестового середовища, поступово оновлюйте залежності та ретельно тестуйте критичні компоненти. NumPy 2.x закладає фундамент для майбутнього Python data science — від free-threaded обчислень до GPU-прискорення через Array API. Час мігрувати — і ця стаття, сподіваюся, зробить цей процес трохи менш страшним.

Про Автора Editorial Team

Our team of expert writers and editors.