From 16ad3be296f51d31530cf1c764970649b2a5a664 Mon Sep 17 00:00:00 2001 From: Carlos Villar Date: Fri, 28 Oct 2022 18:28:49 +0200 Subject: [PATCH 1/6] Added Manhattan distance algorithm, Fixes: #7776 --- maths/manhattan_distance.py | 133 ++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 maths/manhattan_distance.py diff --git a/maths/manhattan_distance.py b/maths/manhattan_distance.py new file mode 100644 index 000000000000..f0508d34d959 --- /dev/null +++ b/maths/manhattan_distance.py @@ -0,0 +1,133 @@ +from typing import Any + + +def manhattan_distance(point_a: list, point_b: list) -> float: + """ + Expectts two list of numbers representing two points in the same + n-dimensional space + + https://en.wikipedia.org/wiki/Taxicab_geometry + + >>> manhattan_distance([1,1], [2,2]) + 2.0 + >>> manhattan_distance([1.5,1.5], [2,2]) + 1.0 + >>> manhattan_distance([1.5,1.5], [2.5,2]) + 1.5 + >>> manhattan_distance([-3, -3, -3], [0, 0, 0]) + 9.0 + >>> manhattan_distance([1,1], None) + Traceback (most recent call last): + ... + ValueError: Missing an input + >>> manhattan_distance([1,1], [2, 2, 2]) + Traceback (most recent call last): + ... + ValueError: Both points must be in the same n-dimensional space + >>> manhattan_distance([1,"one"], [2, 2, 2]) + Traceback (most recent call last): + ... + TypeError: Expected a list of numbers as input, found str + >>> manhattan_distance(1, [2, 2, 2]) + Traceback (most recent call last): + ... + TypeError: Expected a list of numbers as input, found int + >>> manhattan_distance([1,1], "not_a_list") + Traceback (most recent call last): + ... + TypeError: Expected a list of numbers as input, found str + """ + + _validate_point(point_a) + _validate_point(point_b) + if len(point_a) != len(point_b): + raise ValueError("Both points must be in the same n-dimensional space") + + total_distance = 0.0 + for i in range(len(point_a)): + total_distance += abs(point_a[i] - point_b[i]) + + return total_distance + + +def _validate_point(point: Any) -> None: + """ + >>> _validate_point(None) + Traceback (most recent call last): + ... + ValueError: Missing an input + >>> _validate_point([1,"one"]) + Traceback (most recent call last): + ... + TypeError: Expected a list of numbers as input, found str + >>> _validate_point(1) + Traceback (most recent call last): + ... + TypeError: Expected a list of numbers as input, found int + >>> _validate_point("not_a_list") + Traceback (most recent call last): + ... + TypeError: Expected a list of numbers as input, found str + """ + if point: + if isinstance(point, list): + for item in point: + if not (isinstance(item, int) or isinstance(item, float)): + raise TypeError( + f"Expected a list of numbers as input, " + f"found {type(item).__name__}" + ) + else: + raise TypeError( + f"Expected a list of numbers as input, found {type(point).__name__}" + ) + else: + raise ValueError("Missing an input") + + +def manhattan_distance_one_liner(point_a: list, point_b: list) -> float: + """ + Version with one liner + + >>> manhattan_distance_one_liner([1,1], [2,2]) + 2.0 + >>> manhattan_distance_one_liner([1.5,1.5], [2,2]) + 1.0 + >>> manhattan_distance_one_liner([1.5,1.5], [2.5,2]) + 1.5 + >>> manhattan_distance_one_liner([-3, -3, -3], [0, 0, 0]) + 9.0 + >>> manhattan_distance_one_liner([1,1], None) + Traceback (most recent call last): + ... + ValueError: Missing an input + >>> manhattan_distance_one_liner([1,1], [2, 2, 2]) + Traceback (most recent call last): + ... + ValueError: Both points must be in the same n-dimensional space + >>> manhattan_distance_one_liner([1,"one"], [2, 2, 2]) + Traceback (most recent call last): + ... + TypeError: Expected a list of numbers as input, found str + >>> manhattan_distance_one_liner(1, [2, 2, 2]) + Traceback (most recent call last): + ... + TypeError: Expected a list of numbers as input, found int + >>> manhattan_distance_one_liner([1,1], "not_a_list") + Traceback (most recent call last): + ... + TypeError: Expected a list of numbers as input, found str + """ + + _validate_point(point_a) + _validate_point(point_b) + if len(point_a) != len(point_b): + raise ValueError("Both points must be in the same n-dimensional space") + + return float(sum(abs(x - y) for x, y in zip(point_a, point_b))) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 1e923f20bd7135f3e88acc0e1ba5174e603caa04 Mon Sep 17 00:00:00 2001 From: Carlos Villar Date: Sat, 29 Oct 2022 09:25:05 +0200 Subject: [PATCH 2/6] Forgot that isinstance can accept a tuple --- maths/manhattan_distance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/manhattan_distance.py b/maths/manhattan_distance.py index f0508d34d959..26e156ee218d 100644 --- a/maths/manhattan_distance.py +++ b/maths/manhattan_distance.py @@ -72,7 +72,7 @@ def _validate_point(point: Any) -> None: if point: if isinstance(point, list): for item in point: - if not (isinstance(item, int) or isinstance(item, float)): + if not isinstance(item, (int, float)): raise TypeError( f"Expected a list of numbers as input, " f"found {type(item).__name__}" From 76bca3353b2aa487a96751ba5ed84aedbd8a4e78 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 30 Oct 2022 09:18:03 +0100 Subject: [PATCH 3/6] Apply suggestions from code review --- maths/manhattan_distance.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/maths/manhattan_distance.py b/maths/manhattan_distance.py index 26e156ee218d..049207ad4904 100644 --- a/maths/manhattan_distance.py +++ b/maths/manhattan_distance.py @@ -43,14 +43,10 @@ def manhattan_distance(point_a: list, point_b: list) -> float: if len(point_a) != len(point_b): raise ValueError("Both points must be in the same n-dimensional space") - total_distance = 0.0 - for i in range(len(point_a)): - total_distance += abs(point_a[i] - point_b[i]) + return sum(a - b for a, b in zip(point_a, point_b)) - return total_distance - -def _validate_point(point: Any) -> None: +def _validate_point(point: list[float]) -> None: """ >>> _validate_point(None) Traceback (most recent call last): From 6592b504187fd94532f7fabfb865bd9ed97eb5bc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 30 Oct 2022 08:18:55 +0000 Subject: [PATCH 4/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/manhattan_distance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/manhattan_distance.py b/maths/manhattan_distance.py index 049207ad4904..e6e8c7848f2d 100644 --- a/maths/manhattan_distance.py +++ b/maths/manhattan_distance.py @@ -43,7 +43,7 @@ def manhattan_distance(point_a: list, point_b: list) -> float: if len(point_a) != len(point_b): raise ValueError("Both points must be in the same n-dimensional space") - return sum(a - b for a, b in zip(point_a, point_b)) + return sum(a - b for a, b in zip(point_a, point_b)) def _validate_point(point: list[float]) -> None: From 069514819beee3f1a8874fdc368be02d3261804e Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 30 Oct 2022 09:20:24 +0100 Subject: [PATCH 5/6] Update manhattan_distance.py --- maths/manhattan_distance.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/maths/manhattan_distance.py b/maths/manhattan_distance.py index e6e8c7848f2d..0d09f2532c9b 100644 --- a/maths/manhattan_distance.py +++ b/maths/manhattan_distance.py @@ -1,6 +1,3 @@ -from typing import Any - - def manhattan_distance(point_a: list, point_b: list) -> float: """ Expectts two list of numbers representing two points in the same From ae57a5a8513826770292e5eed642073c7ff9446a Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 30 Oct 2022 09:51:51 +0100 Subject: [PATCH 6/6] Update manhattan_distance.py --- maths/manhattan_distance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/manhattan_distance.py b/maths/manhattan_distance.py index 0d09f2532c9b..2711d4c8ccd6 100644 --- a/maths/manhattan_distance.py +++ b/maths/manhattan_distance.py @@ -40,7 +40,7 @@ def manhattan_distance(point_a: list, point_b: list) -> float: if len(point_a) != len(point_b): raise ValueError("Both points must be in the same n-dimensional space") - return sum(a - b for a, b in zip(point_a, point_b)) + return float(sum(abs(a - b) for a, b in zip(point_a, point_b))) def _validate_point(point: list[float]) -> None: