Allclose: примеры (PYTHON)

Использование функции allclose для сравнения массивов в Python
Раздел: NumPy, Сравнение массивов
allclose(a, b[, rtol, atol, equal_nan]): bool

Функция allclose в NumPy

Функция numpy.allclose применяется для проверки приблизительного равенства двух массивов в пределах заданных допусков. Она часто используется в вычислительных задачах, где из-за погрешностей представления чисел с плавающей запятой точное равенство маловероятно, например, в машинном обучении, научных расчетах и обработке сигналов.

Синтаксис функции: numpy.allclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False).

Аргументы функции:

  • a, b (array_like): входные массивы для сравнения.
  • rtol (float): относительный допуск. Это максимально допустимая разница между элементами a и b, выраженная относительно абсолютного значения b (или a). По умолчанию установлено значение 1e-05.
  • atol (float): абсолютный допуск. Это максимально допустимая абсолютная разница между элементами. Значение по умолчанию - 1e-08.
  • equal_nan (bool): флаг, определяющий, должны ли значения NaN считаться равными. Если установлено значение True, то NaN в одном массиве будет считаться равным NaN в другом массиве. По умолчанию False.

Функция возвращает True, если все элементы массивов a и b удовлетворяют условию: |a - b| ≤ (atol + rtol * |b|). В противном случае возвращается False.

Примеры использования allclose

Базовое сравнение двух массивов с допусками по умолчанию.

import numpy as np

a = np.array([1.0, 2.0, 3.0])
b = np.array([1.00001, 2.00001, 3.00001])
result = np.allclose(a, b)
print(result)
True

Использование пользовательских относительного и абсолютного допусков.

import numpy as np

a = np.array([1.0, 2.0, 3.0])
b = np.array([1.1, 2.1, 3.1])
result = np.allclose(a, b, rtol=0.15, atol=0.0)
print(result)
True

Сравнение массивов с разными значениями и проверка влияния абсолютного допуска.

import numpy as np

a = np.array([0.0, 0.0, 0.0])
b = np.array([0.001, 0.001, 0.001])
result = np.allclose(a, b, rtol=1e-05, atol=0.001)
print(result)
True

Использование параметра equal_nan для сравнения массивов, содержащих NaN.

import numpy as np

a = np.array([1.0, np.nan, 3.0])
b = np.array([1.0, np.nan, 3.0])
result_without_nan = np.allclose(a, b, equal_nan=False)
result_with_nan = np.allclose(a, b, equal_nan=True)
print(f'equal_nan=False: {result_without_nan}')
print(f'equal_nan=True: {result_with_nan}')
equal_nan=False: False
equal_nan=True: True

Похожие функции в Python

В Python существует несколько функций для приближенного сравнения, каждая со своей областью применения.

  • numpy.isclose: поэлементное сравнение двух массивов, возвращает булев массив, указывающий, какие элементы близки. Полезно для анализа различий в конкретных позициях.
  • math.isclose: функция из стандартной библиотеки math для сравнения двух скалярных значений с плавающей запятой. Используется, когда работают с отдельными числами, а не с массивами.
  • numpy.array_equal: проверяет точное равенство двух массивов, включая форму и тип данных. Не использует допуски, поэтому подходит для случаев, когда требуется строгое совпадение.
  • numpy.all в комбинации с операторами: можно вручную задать условие сравнения, например, np.all(np.abs(a - b) <= tolerance). Это дает больше гибкости, но требует написания дополнительного кода.

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

Типичные ошибки при использовании allclose

Ошибки часто связаны с непониманием параметров функции или особенностей типов данных.

Использование функции с массивами разной формы приводит к ValueError.

import numpy as np

a = np.array([1, 2, 3])
b = np.array([1, 2])
try:
    result = np.allclose(a, b)
except Exception as e:
    print(f'Ошибка: {type(e).__name__}: {e}')
Ошибка: ValueError: operands could not be broadcast together with shapes (3,) (2,)

Неправильная интерпретация параметров rtol и atol может привести к неожиданным результатам.

import numpy as np

a = np.array([0.0])
b = np.array([0.1])
result = np.allclose(a, b, rtol=1e-05, atol=0.0)
print(f'С atol=0.0: {result}')
result2 = np.allclose(a, b, rtol=1e-05, atol=0.11)
print(f'С atol=0.11: {result2}')
С atol=0.0: False
С atol=0.11: True

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

import numpy as np

a = np.array([1, 2, 3], dtype=int)
b = np.array([1, 2, 4], dtype=int)
result = np.allclose(a, b)
print(result)
False

Попытка сравнить массивы, содержащие нечисловые данные, приводит к TypeError.

import numpy as np

a = np.array([1, 2, 'text'])
b = np.array([1, 2, 'text'])
try:
    result = np.allclose(a, b)
except Exception as e:
    print(f'Ошибка: {type(e).__name__}: {e}')
Ошибка: TypeError: ufunc 'subtract' did not contain a loop with signature matching types (dtype(' dtype('

Изменения в последних версиях

В NumPy 1.10.0 был добавлен параметр equal_nan. До этой версии сравнение массивов, содержащих NaN, всегда возвращало False, так как NaN не равен самому себе. С введением параметра появилась возможность учитывать NaN как равные значения.

В более ранних версиях изменения касались в основном улучшения производительности и исправления ошибок в алгоритмах сравнения. Рекомендуется использовать актуальные версии NumPy для получения всех улучшений.

Расширенные примеры применения allclose

Сравнение многомерных массивов. Функция автоматически обрабатывает массивы любой размерности.

Пример python
import numpy as np

a = np.array([[1.0, 2.0], [3.0, 4.0]])
b = np.array([[1.000009, 2.000009], [3.000009, 4.000009]])
result = np.allclose(a, b)
print(f'Многомерные массивы: {result}')
Многомерные массивы: True

Использование allclose для проверки сходимости итерационных алгоритмов, например, в методе Ньютона.

Пример python
import numpy as np

def newton_method(f, df, x0, tol=1e-6, max_iter=100):
    x = x0
    for i in range(max_iter):
        x_new = x - f(x) / df(x)
        if np.allclose(x_new, x, rtol=0, atol=tol):
            print(f'Сходимость достигнута на итерации {i+1}')
            return x_new
        x = x_new
    return x
f = lambda x: x**2 - 2
df = lambda x: 2*x
root = newton_method(f, df, 1.0)
print(f'Приближенный корень: {root}')
Сходимость достигнута на итерации 6
Приближенный корень: 1.4142135623730951

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

Пример python
import numpy as np

# Предположим, вычислено значение интеграла
computed_value = 2.999999995
# Ожидаемое значение
expected_value = 3.0
# Проверка с учетом погрешности метода
assert np.allclose(computed_value, expected_value, rtol=1e-8, atol=1e-8), "Тест не пройден"
print("Тест пройден успешно")
Тест пройден успешно

Сравнение комплексных чисел. Функция корректно работает с массивами комплексных чисел, сравнивая вещественные и мнимые части отдельно.

Пример python
import numpy as np

a = np.array([1+2j, 3+4j])
b = np.array([1.00001+2.00001j, 3.00001+4.00001j])
result = np.allclose(a, b)
print(f'Комплексные числа: {result}')
Комплексные числа: True

Использование allclose вместе с масками для выборочного сравнения подмассивов.

Пример python
import numpy as np

a = np.array([1.0, 2.0, np.nan, 4.0])
b = np.array([1.0, 2.0, np.nan, 4.00001])
# Создаем маску, исключающую позиции с NaN
mask = ~(np.isnan(a) | np.isnan(b))
result = np.allclose(a[mask], b[mask])
print(f'Сравнение с маской: {result}')
Сравнение с маской: True

Сравнение массивов с разными типами данных, но совместимыми значениями.

Пример python
import numpy as np

a = np.array([1, 2, 3], dtype=np.float32)
b = np.array([1.00001, 2.00001, 3.00001], dtype=np.float64)
result = np.allclose(a, b)
print(f'Разные типы данных: {result}')
Разные типы данных: True

Альтернативы в других языках программирования

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

JavaScript: сравнение с использованием абсолютной разности.

function allClose(arr1, arr2, rtol = 1e-05, atol = 1e-08) {
    if (arr1.length !== arr2.length) return false;
    for (let i = 0; i < arr1.length; i++) {
        if (Math.abs(arr1[i] - arr2[i]) > (atol + rtol * Math.abs(arr2[i]))) {
            return false;
        }
    }
    return true;
}
console.log(allClose([1.0, 2.0], [1.00001, 2.00001]));
true

Java: использование цикла для сравнения элементов массива.

public class AllClose {
    public static boolean allClose(double[] a, double[] b, double rtol, double atol) {
        if (a.length != b.length) return false;
        for (int i = 0; i < a.length; i++) {
            if (Math.abs(a[i] - b[i]) > (atol + rtol * Math.abs(b[i]))) {
                return false;
            }
        }
        return true;
    }
    public static void main(String[] args) {
        double[] a = {1.0, 2.0};
        double[] b = {1.00001, 2.00001};
        System.out.println(allClose(a, b, 1e-05, 1e-08));
    }
}
true

PHP: сравнение массивов с помощью цикла.

function allClose($a, $b, $rtol = 1e-05, $atol = 1e-08) {
    if (count($a) !== count($b)) return false;
    foreach ($a as $i => $val) {
        if (abs($val - $b[$i]) > ($atol + $rtol * abs($b[$i]))) {
            return false;
        }
    }
    return true;
}
$a = [1.0, 2.0];
$b = [1.00001, 2.00001];
echo allClose($a, $b) ? 'true' : 'false';
true

SQL: прямое сравнение значений с допуском в условиях WHERE.

SELECT * FROM table_name
WHERE ABS(column_a - column_b) <= 1e-08 + 1e-05 * ABS(column_b);

C#: использование метода для сравнения массивов.

using System;
class AllClose {
    static bool AllCloseFunc(double[] a, double[] b, double rtol = 1e-05, double atol = 1e-08) {
        if (a.Length != b.Length) return false;
        for (int i = 0; i < a.Length; i++) {
            if (Math.Abs(a[i] - b[i]) > (atol + rtol * Math.Abs(b[i]))) {
                return false;
            }
        }
        return true;
    }
    static void Main() {
        double[] a = { 1.0, 2.0 };
        double[] b = { 1.00001, 2.00001 };
        Console.WriteLine(AllCloseFunc(a, b));
    }
}
True

Lua: итерация по таблицам.

function allClose(a, b, rtol, atol)
    rtol = rtol or 1e-05
    atol = atol or 1e-08
    if #a ~= #b then return false end
    for i = 1, #a do
        if math.abs(a[i] - b[i]) > (atol + rtol * math.abs(b[i])) then
            return false
        end
    end
    return true
end
local a = {1.0, 2.0}
local b = {1.00001, 2.00001}
print(allClose(a, b))
true

Golang: сравнение срезов.

package main
import (
    "fmt"
    "math"
)
func allClose(a, b []float64, rtol, atol float64) bool {
    if len(a) != len(b) {
        return false
    }
    for i := range a {
        if math.Abs(a[i]-b[i]) > (atol + rtol*math.Abs(b[i])) {
            return false
        }
    }
    return true
}
func main() {
    a := []float64{1.0, 2.0}
    b := []float64{1.00001, 2.00001}
    fmt.Println(allClose(a, b, 1e-05, 1e-08))
}
true

Kotlin: использование встроенных функций для массивов.

fun allClose(a: DoubleArray, b: DoubleArray, rtol: Double = 1e-05, atol: Double = 1e-08): Boolean {
    if (a.size != b.size) return false
    return a.indices.all { i ->
        Math.abs(a[i] - b[i]) <= (atol + rtol * Math.abs(b[i]))
    }
}
fun main() {
    val a = doubleArrayOf(1.0, 2.0)
    val b = doubleArrayOf(1.00001, 2.00001)
    println(allClose(a, b))
}
true

питон allclose function comments

En
Allclose Check if arrays are element-wise equal