Это продолжение моего предыдущего вопроса Пользователь glebcom помог мне с переносом координат из строки в список значений float64. В ответе я нашел 2 метода, как рассчитать расстояние между координатами:
import polars as pl
from scipy.spatial import distance
import numpy as np
pl.Config.set_fmt_str_lengths(2000)
data = {"a": ["782.83 7363.51 6293 40 PD","850.68 7513.1 6262.17 40 PD"], "b": ["795.88 7462.65 6293 40 PD","1061.64 7486.08 6124.85 40 PD"]}
df=pl.DataFrame(data)
df=df.with_columns([
pl.col("a").str.replace_all(r" +", " ")\
.str.split(" ").arr.slice(0,3)\
.cast(pl.List(pl.Float64)).alias("c"),\
pl.col("b").str.replace_all(r" +", " ")\
.str.split(" ").arr.slice(0,3)\
.cast(pl.List(pl.Float64)).alias("d")\
])
print(df)
Мои попытки были
df=df.with_columns(np.linalg.norm(pl.col("C")-pl.col("d")).alias("distance"))
or
df=df.with_columns(distance(pl.col("C"),pl.col("d")).alias("distance"))
но ничего из вышеперечисленного не работает. Заранее спасибо за вашу помощь.
Артур






Вы не сможете вызвать numpy.linalg.norm непосредственно в своем фрейме данных polars. Он ожидает пустой массив формы (N, n) (где N — ваше количество точек, а n — ваше число измерений, 3).
Вы можете подготовить данные самостоятельно, передать их в numpy и вернуть результаты в поляры.
Сначала вычислите разницу между координатами двух ваших точек по всем трем измерениям:
diffs = df.select(
[
(pl.col("c").arr.get(i) - pl.col("d").arr.get(i)).alias(f"diff_{i}")
for i in range(3)
]
)
┌─────────┬────────┬────────┐
│ diff_0 ┆ diff_1 ┆ diff_2 │
│ --- ┆ --- ┆ --- │
│ f64 ┆ f64 ┆ f64 │
╞═════════╪════════╪════════╡
│ -13.05 ┆ -99.14 ┆ 0.0 │
│ -210.96 ┆ 27.02 ┆ 137.32 │
└─────────┴────────┴────────┘
Затем преобразуйте его в numpy и вызовите функцию:
import numpy.linalg
distance=numpy.linalg.norm(diffs.to_numpy(), axis=1)
pl.Series(distance).alias("distance")
┌────────────┐
│ distance │
│ --- │
│ f64 │
╞════════════╡
│ 99.99521 │
│ 253.161973 │
└────────────┘
В качестве альтернативы вы можете самостоятельно рассчитать евклидов продукт:
df.select(
[
(pl.col("c").arr.get(i) - pl.col("d").arr.get(i)).alias(f"diff_{i}") ** 2
for i in range(3)
]
).sum(axis=1).sqrt()
┌────────────┐
│ distance │
│ --- │
│ f64 │
╞════════════╡
│ 99.99521 │
│ 253.161973 │
└────────────┘
PS: scipy.spatial.distance.euclidean не будет работать, потому что он работает только с одной точкой во времени, что сделало бы его очень медленным в полярах.
Решение с np.linalg.norm внутри map
def l2_norm(s: pl.Series) -> pl.Series:
# 1) difference: c-d
diff = s.struct.field("c").to_numpy() - s.struct.field("d").to_numpy()
# 2) apply np.linalg.norm()
return pl.Series(diff).apply(
lambda x: np.linalg.norm(np.array(x))
)
df.with_columns([
pl.struct(["c", "d"]).map(l2_norm).alias("distance")
])
┌────────────┐
│ distance │
│ --- │
│ f64 │
╞════════════╡
│ 99.99521 │
│ 253.161973 │
└────────────┘
Спасибо за ответ. Жаль, что нельзя отметить два ответа как решения.