【数据挖掘】时间序列的傅里叶变换:用numpy解释的快速卷积

一、说明

        本篇告诉大家一个高级数学模型,即傅里叶模型的使用; 当今,傅里叶变换及其所有变体构成了我们现代世界的基础,为压缩、通信、图像处理等技术提供了动力。我们从根源上理解,从根本上应用,这是值得付出的代价。

二、FFT的历史根源

        傅里叶变换算法被认为是所有数学中最伟大的发现之一。法国数学家让-巴蒂斯特·约瑟夫·傅立叶在1822年的《Théorie analytique de la chaleur》一书中为调和分析奠定了基础。

        法国数学家让·巴蒂斯特·约瑟夫·傅立叶(1768-1830 年)的雕刻肖像,19 世纪初。[来源:维基百科,图片来自公共领域]

        这个奇妙的框架还为分析时间序列提供了很好的工具......这就是我们在这里的原因!

        这篇文章是傅里叶变换系列文章的一部分。今天我们将讨论卷积以及傅里叶变换如何提供最快的方法。

        

三、离散傅里叶变换 (DFT) 的定义

        让我们从基本定义开始。N 个元素的离散时间序列 x 的离散傅里叶变换为:

        离散傅里叶变换 (DFT) 定义。存在其他定义,您只需要选择一个并坚持下去(由作者制作)

        其中 k 表示 x 频谱的第 k 个频率。请注意,一些作者在该定义中添加了 1/N 的比例因子,但对这篇文章来说并不重要——总而言之,这只是一个定义问题并坚持下去。

        然后是傅里叶逆变换(给定前向傅里叶变换的定义):

        逆离散傅里叶变换,基于上述前向定义(由作者制作)。

        话虽如此,傅里叶变换最重要的定理之一是一个空间中的卷积等价于另一个空间中的乘法。换句话说,乘积的傅里叶变换是相应傅里叶谱的卷积,卷积的傅里叶变换是相应傅里叶谱的乘积。

        时域中的乘法对应于傅里叶域中的循环卷积(由作者制作)。

        和

        时域中的循环卷积对应于傅里叶域中的乘法(由作者制作)。

        其中点表示标准乘积(乘法),圆圈星表示圆形卷积

        两个重要注意事项:

  • 周期信号:傅里叶分析框架认为我们处理的信号是周期性的。换句话说,它们从负无穷大重复到无穷大。然而,使用有限的内存计算机处理此类信号并不总是实用的,因此我们只“玩”一个周期,我们将在后面看到。
  • 循环卷积:卷积定理指出乘法等价于循环卷积,这与我们更习惯的线性卷积略有不同。正如我们将看到的,它并没有那么不同,也没有那么复杂。

四、循环卷积与线性卷积

        如果您熟悉线性卷积(通常简称为“卷积”),那么您不会被循环卷积混淆。基本上,循环卷积只是卷积周期信号的方法。正如您可以猜到的,线性卷积仅对有限长度的信号有意义,这些信号的范围不是从负无穷大到无穷大。在我们的例子中,在傅里叶分析的上下文中,我们的信号是周期性的,因此不满足这个条件。我们不能谈论(线性)卷积。

        然而,我们仍然可以直观地对周期信号进行线性卷积式操作:只需将周期信号卷积在一个周期长度上即可。这就是循环卷积的作用:它在一个周期跨度上卷积 2 个相同长度的周期信号。

为了进一步说服自己差异,请比较离散线性卷积和离散循环卷积的两个公式:

线性卷积方程:大多数时候在信号处理中,使用此公式,通过填充零(由作者制作)。

循环卷积 :这是处理周期信号时使用的卷积,如傅里叶分析(由作者制作)。

注意差异:

边界:线性卷积使用从负无穷大到正无穷大的样本 — 如前所述,在这种情况下,x 和 y 具有有限的能量,总和是有意义的。对于循环卷积,我们只需要在一个时间段内发生了什么,所以总和只跨越一个周期

- 循环索引 :在循环卷积中,我们使用长度为 N 的模运算“包装”y 的索引。这只是一种确保 y 被认为是周期为 N 的周期的方法:当我们想知道位置 k 处 y 的值时,我们只在位置 k%N 处使用 y 的值 — 因为 y 是 N 周期性的,我们得到正确的值。同样,这只是处理周期性无限长度样本序列的一种数学方法。

五、在 numpy 中的实现

        Numpy为有限长度信号提供了很好的工具:这是一个好消息,因为正如我们刚刚看到的,我们的无限长度周期信号可以用一个周期来表示。

        让我们创建一个简单的类来表示这些信号。我们添加了一个快速绘制数组的方法,以及“基本”数组前后的额外周期,以记住我们正在处理周期序列。

import numpy as np
import matplotlib.pyplot as plt

class PeriodicArray:
    """A class to represent a periodic signal, using a single
    period of the sequence.
    """

    def __init__(self, base):
        """base is the base sequence representing a full period."""
        self.base = base
    
    @property
    def N(self): 
        """Lenght of the base array, which is also the 
        period of our infinite-periodic sequence"""
        return len(self.base)
    
    def __getitem__(self, n):
        """We can get the value at any index, from -infinity
        to +infinity using the fact that the sequence is N-
        periodic, so we use the modulo operator.
        
        Examples
        --------
        >>> x = PeriodicArray([1, 2, 3])
        >>> x[0]
        1
        >>> x[4]
        2
        >>> x[5]
        3
        """
        return self.base[n%self.N]
    
    def plot(self, ax=None):
        """Quickly plot the sequence, with a period before and after
        the base array."""
        if ax is None:
            fig, ax = plt.subplots()
        line = ax.plot(self.base, '-o')
        ax.plot(np.arange(-self.N, 0), self.base, '--o', color=line[0].get_color(), alpha=0.5)
        ax.plot(np.arange(self.N, self.N*2), self.base, '--o', color=line[0].get_color(), alpha=0.5)
        return ax

        让我们看两个例子:首先是采样的窦序列,然后是线性序列。两者都被认为是 N 周期性的(在这种情况下为 N=10)。

periodic_sampled_sinus = PeriodicArray(np.sin(2*np.pi*1*np.linspace(0, np.pi/10, 10)))
periodic_sampled_sinus.plot()


periodic_slope = PeriodicArray(np.linspace(-5, 5, num=10)*0.5)
periodic_slope.plot()

PeriodicArray 的 2 个示例:“基”周期以深蓝色从 0 到 N 绘制,而其他 2 个周期在前后添加,以表示我们正在处理周期序列的事实(由作者制作)。

六、循环卷积,慢速方式

        现在让我们实现上面看到的循环卷积方程。使用索引和模运算符,非常简单:

        上述2个周期序列之间的循环卷积(由作者制作)。

        太好了,我们现在可以看到两个信号之间的循环卷积是什么样子的。将所有内容放在一个数字中:

        左:第一个周期数组。中间:第二周期数组。右:2个周期数组的循环卷积,这也是一个周期数组(由作者制作)。

        现在这个解决方案运行得很好,但它有一个主要缺陷:它很慢。如您所见,我们必须经历 2 个嵌套循环来计算结果:一个用于结果数组中的每个位置,一个用于计算该位置的结果:我们说算法是 O(N²),随着 N 的增加,操作次数将随着 N 的平方而增加。

        对于示例中的小型数组,这不是问题,但随着数组的增长,它将成为主要问题。

        此外,在python中,对数值数据的循环在大多数情况下被认为是一种不好的做法。一定有更好的方法...

七、循环卷积,傅里叶方式

        这就是傅里叶变换和卷积定理发挥作用的地方。由于离散傅里叶变换的实现方式,使用快速傅里叶变换(FFT)以非常快速和优化的方式实现,操作非常(我们说FFT是O(N log N),这比O(N²)要好得多)。

        使用卷积定理,我们可以利用 2 个序列的 DFT 的乘积,当使用逆 DFT 转换回时域时,我们得到输入时间序列的卷积。换句话说,我们有:

        使用直接和逆傅里叶变换的x和y之间的循环卷积(由作者制作)。

        其中DFT表示离散傅里叶变换,IDFT表示逆运算。

        然后我们可以非常轻松地实现这个算法来计算 x 和 y 的卷积:

def circconv_fast(x, y):
    """Fast circular convolution using DFT.
    Return the full array of the circulard 
    convolution between x and y.
    """
    X = np.fft.fft(x)
    Y = np.fft.fft(y)
    return np.real(np.fft.ifft(np.multiply(X, Y)))

# let's compute the circular convolution for our 2 signals
circ_fast = circconv_fast(periodic_sampled_sinus.base, periodic_slope.base)
circ_fast = PeriodicArray(circ_fast)

八、数值和时间比较

        最后,让我们验证这两种方法是否产生相同的结果,并比较 python 使用这两种技术计算循环卷积所需的时间:

# compare both ways : "slow" way, and DFT-way
fig, ax = plt.subplots()
ax.plot(circ.base, '-o', label="slow-way")
ax.plot(circ_fast.base, '-o', label="DFT-way")
ax.legend()
ax.set_title('Comparison of 2 ways to compute convolution : \nslow-algebraic way VS using DFT and the convolution theorem')

        比较两种计算两个周期序列之间循环卷积的方法:“慢速方式”是使用蓝色循环和加法的简单代数,它与橙色的“傅里叶方式”叠加。两种方法给出的结果完全相同(精确到数值精度)(由作者制作)。

        这是完美的搭配!两者在数值方面是严格等效的。

        现在进行时间比较:

N = 1000
long_x = np.sin(2*np.pi*1*np.linspace(0, np.pi/10, N))
long_y = np.cos(2*np.pi*1*np.linspace(0, np.pi/10, N))

print(circconv(long_x, long_y))
print(circconv_fast(long_x, long_y))
# first make sure that both method yield the same result
assert np.allclose(circconv(long_x, long_y), circconv_fast(long_x, long_y))

%timeit circconv(long_x, long_y)
%timeit circconv_fast(long_x, long_y)

# for N = 10   :  90.2 µs ± 10.2 µs for the slow way VS 14.1 µs ± 161 ns  for the DFT-way
# for N = 1000 : 579   ms ± 9.14 ms for the slow way VS 69.4 µs ± 2.35 µs for the DFT-way

from physipy import units
ms = units['ms']
mus = units['mus']
print("Gain in speed for 10 samples length: ", 90*mus/(14*mus))
print("Gain in speed for 1000 samples length: ", 579*ms/(69*mus))

结果是:

  • 对于 N=10 个样本,DFT 快 6 倍
  • 对于 N=1000 个样本,DFT 的速度快约 10000 倍

这是巨大的!现在考虑一下,当您分析包含成千上万个样本的时间序列时,它可以为您带来什么!

Fourier Transform for Time Series: Fast Convolution Explained with numpy | by Yoann Mocquin | Jul, 2023 | Towards Data Science

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/43342.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HTML5——基础知识及使用

HTML 文件基本结构 <html><head><title>第一个页面</title></head><body>hello world</body> </html> html 标签是整个 html 文件的根标签(最顶层标签).head 标签中写页面的属性.body 标签中写的是页面上显示的内容.title 标…

实现外部缓存-Redis

目录 实现 RedisTemplate RedisTemplate的序列化 RedisSerializer 创建Redis缓存配置类 测试使用 创建配置类 创建注解测试实体 创建配置文件 创建单元测试类进行测试 实现 RedisTemplate XXXTemplate 是 Spring 的一大设计特色&#xff0c;其中&#xff0c;RedisTe…

Mybatis操作数据库执行流程的先后顺序是怎样的?

MyBatis是一个支持普通SQL查询、存储及高级映射的持久层框架&#xff0c;它几乎消除了JDBC的冗余代码。使Java开发人员可以使用面向对象的编程思想来操作数据库。对于MyBatis的工作原理和操作流程的理解&#xff0c;我们先来看下面的工作流程图。 MaBatis的工作流程 在上图中…

element的el-upload实现多个图片上传以及预览与删除

<el-form-itemlabel"实验室照片:"prop"labUrlList"v-if"ruleForm.labHave"><el-upload:action"urlUpload":headers"loadHeader"list-type"picture-card":file-list"ruleForm.labUrlList"class…

【el-tree查询并高亮】vue使用el-tree组件,搜索展开并选中对应节点,高亮搜索的关键字,过滤后高亮关键字,两种方法

第一种&#xff08;直接展开并高亮关键字&#xff09; 效果图这样的&#xff0c;会把所有的有这些关键字的节点都展开 代码&#xff1a; 这里的逻辑就是通过递归循环把所有和关键字匹配的节点筛选出来 然后通过setCheckedKeys方法把他展开选中 然后通过filterReal把关键字高亮…

数据库redis作业

数据库redis作业 redis9种数据类型的基本操作 redis持久化&#xff1a;分别启用rdb和aof&#xff0c;并查看是否有对应文件生成 作业1&#xff1a;redis9种数据类型的基本操作 1、key操作 key * #查询所有的key keys *exists 参数 #参数&#xff1a;key #判断该key是否存…

【ADS】ADS复制原理图或版图到另一个工程

直接Ctrl CCtrl V无法粘贴 可以先导入要复制的工程 加入工程&#xff0c;复制完后在勾掉工程

单独在文件中打开allure生成的index.html报告时却显示为loading

【问题描述】&#xff1a;单独在文件中打开allure生成的index.html报告时显示为loading&#xff0c;如下图&#xff1a; 【问题定位】&#xff1a;其实在allure-report下index.html文件是不能直接打开的&#xff0c;出现页面都是loading的情况&#xff0c;这是因为直接allure报…

Rust 数据类型 之 类C枚举 c-like enum

目录 枚举类型 enum 定义和声明 例1&#xff1a;Color 枚举 例2&#xff1a;Direction 枚举 例3&#xff1a;Weekday 枚举 类C枚举 C-like 打印输出 强制转成整数 例1&#xff1a;Weekday 枚举 例2&#xff1a;HttpStatus 枚举 例3&#xff1a;Color 枚举 模式匹配…

使用 Docker 快速上手官方版 LLaMA2 开源大模型

本篇文章&#xff0c;我们聊聊如何使用 Docker 容器快速上手 Meta AI 出品的 LLaMA2 开源大模型。 写在前面 昨天特别忙&#xff0c;早晨申请完 LLaMA2 模型下载权限后&#xff0c;直到晚上才顾上折腾了一个 Docker 容器运行方案&#xff0c;都没来得及写文章来聊聊这个容器怎…

wxchart 小程序 线条图不显示y轴的网格线 (分割线)

如下图&#xff1a;项目需求不显示包括x轴的6条灰色分割线。 分析&#xff1a; 看了一下源码已经写死了是5条分割线&#xff0c;加一条x轴刻度线。没给公开配置方法。 解决方案&#xff1a; 既然没有配置项目&#xff0c;可以转变思路&#xff0c;把这些线条配置成白色&…

Spring整合Mybatis原理

首先介绍一下Mybatis的工作原理 先简略的放两张图&#xff0c;后面的知识结合这两张图比较好理解 Mybatis的基本工作原理 在 Mybatis 中&#xff0c;我们可以使用⼀个接口去定义要执行sql&#xff0c;简化代码如下&#xff1a; 定义⼀个接口&#xff0c;Select 表示要执行查询…

【UniApp开发小程序】”我的“界面实现+“信息修改“界面实现+登出账号实现+图片上传组件【基于若依管理系统开发】

文章目录 界面实现界面效果我的修改信息 “我的”界面实现api页面退出账号让自我介绍只显示一行&#xff0c;结尾多余的字使用...代替跳转到信息修改页面 信息修改界面实现api页面动态给对象设置属性名和值修改密码图片上传组件 部分后端代码Controller 界面实现 界面效果 我…

Windows11的VTK安装:VS201x+Qt5/Qt6 +VTK7.1/VTK9.2.6

需要提前安装好VS2017和VS2019和Qt VS开发控件以及Qt VS-addin。 注意Qt6.2.4只能跟VTK9.2.6联合编译&#xff08;目前VTK9和Qt6的相互支持版本&#xff09;。 首先下载VTK&#xff0c;需要下载源码和data&#xff1a; Download | VTKhttps://vtk.org/download/ 然后这两个文…

1 请使用js、css、html技术实现以下页面,表格内容根据查询条件动态变化。

1.1 创建css文件&#xff0c;用于编辑style 注意&#xff1a; 1.背景颜色用ppt的取色器来获取&#xff1a; 先点击ppt的形状轮廓&#xff0c;然后点击取色器&#xff0c;吸颜色&#xff0c;然后再点击形状轮廓的其他轮廓颜色&#xff0c;即可获取到对应颜色。 2.表格间的灰色线…

什么是搜索引擎?2023 年搜索引擎如何运作?

目录 什么是搜索引擎&#xff1f;搜索引擎的原理什么是搜索引擎爬取&#xff1f;什么是搜索引擎索引&#xff1f;什么是搜索引擎检索?什么是搜索引擎排序&#xff1f; 搜索引擎的目的是什么&#xff1f;搜索引擎如何赚钱&#xff1f;搜索引擎如何建立索引?网页抓取文本处理建…

【数字图像处理与应用】模板匹配

【数字图像处理与应用】模板匹配 题目模板匹配原理Matlab代码实现算法介绍显示图像的匹配结果 (最匹配的一个)MATLAB实现运行结果图像的相关值结果&#xff1a;在原图像上绘制检测到的目标位置&#xff1a;显示检测到的目标坐标&#xff1a; 显示图像的匹配结果 (最匹配的三个&…

聊聊spring-cloud的负载均衡

聊聊spring-cloud的负载均衡 1. 选择合适的负载均衡算法2. 合理设置超时时间3. 缓存服务实例列表4. 使用断路器5. 使用缓存Spring Cloud负载均衡组件对比RibbonLoadBalancerWebClient对比 总结 在微服务架构中&#xff0c;负载均衡是非常重要的一个环节&#xff0c;可以有效地提…

python与深度学习(六):CNN和手写数字识别二

目录 1. 说明2. 手写数字识别的CNN模型测试2.1 导入相关库2.2 加载数据和模型2.3 设置保存图片的路径2.4 加载图片2.5 图片预处理2.6 对图片进行预测2.7 显示图片 3. 完整代码和显示结果4. 多张图片进行测试的完整代码以及结果 1. 说明 本篇文章是对上篇文章训练的模型进行测试…

极速跳板机登陆服务器

目录 一&#xff1a;简单登陆跳板器二&#xff1a;一键申请相关的服务器权限三&#xff1a;简化登陆 一&#xff1a;简单登陆跳板器 登陆公司提供的网址&#xff0c; 下载自己的专属RSA密钥。在密钥文件处&#xff0c; 执行登陆指令&#xff1a; ssh -p 36000 -i id_rsa 用户跳…