numpy 我怎样才能做一个窗口和一个常向量的点积在极坐标中?

kulphzqa  于 11个月前  发布在  其他
关注(0)|答案(3)|浏览(73)

我试图在我的polars DataFrame中为所有窗口计算窗口和静态数组之间的点积,我正在努力找出正确的方法来做到这一点。这里有人对我有任何提示吗?这里是一个快速工作的例子:

import polars as pl
import numpy as np

dummy_data = {
    "id_": [1, 2, 3, 4, 5, 6, 7, 8],
    "value": [1, 1, 2, 2, 3, 3, 4, 4]
}
const = np.array([.5, .5])

df_ = pl.DataFrame(dummy_data)
df_ = df_.set_sorted("id_")

# This panics: Cannot apply operation on arrays of different lengths
df_.rolling("id_", period="2i").agg(pl.col("value").dot(pl.lit(const)))

expected = {
    "id_": [1, 2, 3, 4, 5, 6, 7, 8],
    "value": [None, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0]
}

字符串
我所期望的是在上面的例子中发生以下计算:

np.dot([1.0, 1.0], const)
np.dot([1.0, 2.0], const)
np.dot([2.0, 2.0], const)
etc.


大概这不工作,因为第一个窗口只有1个值,而不是2?至少这是什么,它似乎对我来说。任何人都有一个更好的方法来做到这一点?

x4shl7ld

x4shl7ld1#

在收到一些答案后,找到了我自己的答案,我想分享一些基准。
以下是问题的3种可能解决方案(通过添加一个组列,稍微调整以提供对问题的更全面的了解):
数据类型:

groups = []
for i in range(n):
    for j in range(m):
        groups.append(i)
dummy_data = {
    "id_": list(range(0, m)) * n,
    "group": groups,
    "value": [random.randint(1, 50) / 100 for _ in range(n*m)]
}
vec = np.array([random.randint(1, 100) / 100 for _ in range(x)])

df_ = pl.DataFrame(dummy_data)
df_ = df_.sort("id_")

字符串
在极地不和的牧场有礼貌:

df_.with_columns(
    pl.col("value").cast(pl.Float32).rolling_map(
        lambda x: sum([x[i]*vec[i] for i in range(len(x))]) , window_size=len(vec)
    ).over("group")
)


rolling礼貌@迪恩麦格雷戈:

(
    df_.lazy()
    .drop('value')
    .join(
        df_.lazy()
        .rolling("id_", by="group", period=f"{len(vec)}i")
        .agg(pl.col("value"))
        .filter(pl.col('value').list.len()==len(vec))
        .with_row_count('i')
        .with_columns(const=pl.Series(vec).implode())
        .explode('value', 'const')
        .group_by('i')
        .agg(
            id_=pl.col('id_').first(),
            group=pl.col("group").first(),
            value=(pl.col('value').cast(pl.Float64)).dot(pl.col('const'))
            )
        .drop('i'),
         on=['id_', "group"], how='left'
    )
).collect()


shift,这是我提出的解决方案:

df_.lazy().with_columns(
    res = pl.sum_horizontal([pl.col("value").shift(i).over("group") * vec[i] for i in range(len(vec))])
).select(
    "id_",
    pl.when(pl.col("id_") < len(vec))
    .then(None)
    .otherwise(pl.col("res"))
    .alias("res")
).collect()


每一个都提供了完全相同的答案。下面是几个不同大小的数据集上每一个的计算时间:

###################
n=10
m=10
x=2

rolling_map: 826 µs ± 96.6 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
rolling: 387 µs ± 46.1 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
shift: 160 µs ± 6.37 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

###################
n=10000
m=10
x=10

rolling_map: 119 ms ± 2.18 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
rolling: 8.22 ms ± 170 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
shift: 24.3 ms ± 1.26 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

###################
n=10
m=10000
x=10

rolling_map: 1.01 s ± 7.18 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
rolling: 25.5 ms ± 318 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
shift: 5.3 ms ± 119 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

###################
n=1000
m=1000
x=100

rolling_map: > 60 seconds
rolling: 2.34 s ± 225 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
shift: 731 ms ± 28.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

###################


如果其他人需要运行类似的计算,希望这些数字是有用的。正确的选择取决于所使用的数据和代码的可读性。

fd3cxomn

fd3cxomn2#

你有几个问题要解决。第一个,你已经发现了,第一个窗口只有1个值而不是2。你可以通过稍后移动dot操作来解决这个问题,这样你就可以进行过滤。你还将遇到一个问题,即const如何不作为pl.lit(const)进行广播,或者mul如何不对列表进行操作。为了解决这个问题,您需要添加列来表示行索引和const。从那里您可以分解valueconst列。完成后,你没有任何列表列,可以执行普通的group_by/agg来让点工作。要得到你想要的最终结果,你需要将所有这些都连接到原始框架中(减去要替换的值列)。

(
    df_
    .drop('value')
    .join(
        df_
        .rolling("id_", period="2i")
        .agg(pl.col("value"))
        .filter(pl.col('value').list.len()==2)
        .with_row_count('i')
        .with_columns(const=pl.Series(const).implode())
        .explode('value', 'const')
        .group_by('i')
        .agg(
            id_=pl.col('id_').first(),
            value=(pl.col('value').cast(pl.Float64)).dot(pl.col('const'))
            )
        .drop('i'),
         on='id_', how='left'
    )
)
shape: (8, 2)
┌─────┬───────┐
│ id_ ┆ value │
│ --- ┆ ---   │
│ i64 ┆ f64   │
╞═════╪═══════╡
│ 1   ┆ null  │
│ 2   ┆ 1.0   │
│ 3   ┆ 1.5   │
│ 4   ┆ 2.0   │
│ 5   ┆ 2.5   │
│ 6   ┆ 3.0   │
│ 7   ┆ 3.5   │
│ 8   ┆ 4.0   │
└─────┴───────┘

字符串

hgqdbh6s

hgqdbh6s3#

我不能说我完全理解为什么下面的工作-它几乎看起来像一个Polars错误?因为你的const是相同的两个元素,下面的工作。

df_.rolling("id_", period="2i", offset="-2i").agg(
    x=pl.when(pl.col("value").len() < 2).then(None).otherwise(pl.col("value").dot(0.5)),
    y=pl.col("value"),
).collect()

个字符
似乎pl.col("value").dot(0.5)通过pl.col("value")“广播”了0.5。如果我用pl.Series([0.5, 0.5])替换0.5,那么我得到PanicException: Cannot apply operation on arrays of different lengths
这对我来说毫无意义,所以我假设Polars中有一个bug。

相关问题