用 python 实现小波变换的简单示例
最编程
2024-05-26 16:42:37
...
国产数据库圈,为啥那么多水货?”
最近工作需要,看了一下小波变换方面的东西,用python实现了一个简单的小波变换类,将来可以用在工作中。
简单说几句原理,小波变换类似于傅里叶变换,都是把函数用一组正交基函数展开,选取不同的基函数给出不同的变换。例如傅里叶变换,选择的是sin和cos,或者exp(ikx)这种复指数函数;而小波变换,选取基函数的方式更加灵活,可以根据要处理的数据的特点(比如某一段上信息量比较多),在不同尺度上采用不同的频宽来对已知信号进行分解,从而尽可能保留多一点信息,同时又避免了原始傅里叶变换的大计算量。以下计算采用的是haar基,它把函数分为2段(A1和B1,但第一次不分),对第一段内相邻的2个采样点进行变换(只考虑A1),变换矩阵为
sqrt(0.5) sqrt(0.5)
sqrt(0.5) -sqrt(0.5)
变换完之后,再把第一段(A1)分为两段,同样对相邻的点进行变换,直到无法再分。
下面直接上代码
Wavelet.py
import math
class wave:
def __init__(self):
M_SQRT1_2 = math.sqrt(0.5)
self.h1 = [M_SQRT1_2, M_SQRT1_2]
self.g1 = [M_SQRT1_2, -M_SQRT1_2]
self.h2 = [M_SQRT1_2, M_SQRT1_2]
self.g2 = [M_SQRT1_2, -M_SQRT1_2]
self.nc = 2
self.offset = 0
def __del__(self):
return
class Wavelet:
def __init__(self, n):
self._haar_centered_Init()
self._scratch = []
for i in range(0,n):
self._scratch.append(0.0)
return
def __del__(self):
return
def transform_inverse(self, list, stride):
self._wavelet_transform(list, stride, -1)
return
def transform_forward(self, list, stride):
self._wavelet_transform(list, stride, 1)
return
def _haarInit(self):
self._wave = wave()
self._wave.offset = 0
return
def _haar_centered_Init(self):
self._wave = wave()
self._wave.offset = 1
return
def _wavelet_transform(self, list, stride, dir):
n = len(list)
if (len(self._scratch) < n):
print("not enough workspace provided")
exit()
if (not self._ispower2(n)):
print("the list size is not a power of 2")
exit()
if (n < 2):
return
if (dir == 1): # 正变换
i = n
while(i >= 2):
self._step(list, stride, i, dir)
i = i>>1
if (dir == -1): # 逆变换
i = 2
while(i <= n):
self._step(list, stride, i, dir)
i = i << 1
return
def _ispower2(self, n):
power = math.log(n,2)
intpow = int(power)
intn = math.pow(2,intpow)
if (abs(n - intn) > 1e-6):
return False
else:
return True
def _step(self, list, stride, n, dir):
for i in range(0, len(self._scratch)):
self._scratch[i] = 0.0
nmod = self._wave.nc * n
nmod -= self._wave.offset
n1 = n - 1
nh = n >> 1
if (dir == 1): # 正变换
ii = 0
i = 0
while (i < n):
h = 0
g = 0
ni = i + nmod
for k in range(0, self._wave.nc):
jf = n1 & (ni + k)
h += self._wave.h1[k] * list[stride*jf]
g += self._wave.g1[k] * list[stride*jf]
self._scratch[ii] += h
self._scratch[ii + nh] += g
i += 2
ii += 1
if (dir == -1): # 逆变换
ii = 0
i = 0
while (i < n):
ai = list[stride*ii]
ai1 = list[stride*(ii+nh)]
ni = i + nmod
for k in range(0, self._wave.nc):
jf = n1 & (ni + k)
self._scratch[jf] += self._wave.h2[k] * ai + self._wave.g2[k] * ai1
i += 2
ii += 1
for i in range(0, n):
list[stride*i] = self._scratch[i]
测试代码如下:
test.py
import math
import Wavelet
waveletn = 256
waveletnc = 20 #保留的分量数
wavelettest = Wavelet.Wavelet(waveletn)
waveletorigindata = []
waveletdata = []
for i in range(0, waveletn):
waveletorigindata.append(math.sin(i)*math.exp(-math.pow((i-100)/50,2))+1)
waveletdata.append(waveletorigindata[-1])
wavelettest.transform_forward(waveletdata, 1)
newdata = sorted(waveletdata, key = lambda ele: abs(ele), reverse=True)
for i in range(waveletnc, waveletn): # 筛选出前 waveletnc个分量保留
for j in range(0, waveletn):
if (abs(newdata[i] - waveletdata[j]) < 1e-6):
waveletdata[j] = 0.0
break
wavelettest.transform_inverse(waveletdata, 1)
waveleterr = 0.0
for i in range(0, waveletn):
print(waveletorigindata[i], ",", waveletdata[i])
waveleterr += abs(waveletorigindata[i] - waveletdata[i])/abs(waveletorigindata[i])
print("error: ", waveleterr/waveletn)
当waveletnc = 20时,可得到下图,误差大约为2.1
当waveletnc = 100时,则为下图,误差大约为0.04
当waveletnc = 200时,得到下图,误差大约为0.0005
推荐阅读
-
Python+Matplotlib 显示通用函数、变换和函数的简单示例
-
用Python实现的傅里叶变换及其应用(二)
-
用OpenFeign实现负载均衡的简单示例
-
用Python实现汉诺塔游戏的简单步骤
-
用Python实现单纯形法计算的简单步骤指南
-
用PHP实现简单易懂的递归文件夹复制示例代码
-
用PHP实现简单易懂的递归遍历所有文件与目录的代码示例
-
go语言Socket编程-Socket编程 什么是Socket Socket,英文含义是插座、插孔,一般称之为套接字,用于描述IP地址和端口。可以实现不同程序间的数据通信。 Socket起源于Unix,而Unix基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现,网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用:Socket,该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。 套接字的内核实现较为复杂,不宜在学习初期深入学习,了解到如下结构足矣。 套接字通讯原理示意 在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。 常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。 网络应用程序设计模式 C/S模式 传统的网络应用设计模式,客户机(client)/服务器(server)模式。需要在通讯两端各自部署客户机和服务器来完成数据通信。 B/S模式 浏览器(Browser)/服务器(Server)模式。只需在一端部署服务器,而另外一端使用每台PC都默认配置的浏览器即可完成数据的传输。 优缺点 对于C/S模式来说,其优点明显。客户端位于目标主机上可以保证性能,将数据缓存至客户端本地,从而提高数据传输效率。且,一般来说客户端和服务器程序由一个开发团队创作,所以他们之间所采用的协议相对灵活。可以在标准协议的基础上根据需求裁剪及定制。例如,腾讯所采用的通信协议,即为ftp协议的修改剪裁版。 因此,传统的网络应用程序及较大型的网络应用程序都首选C/S模式进行开发。如,知名的网络游戏魔兽世界。3D画面,数据量庞大,使用C/S模式可以提前在本地进行大量数据的缓存处理,从而提高观感。 C/S模式的缺点也较突出。由于客户端和服务器都需要有一个开发团队来完成开发。工作量将成倍提升,开发周期较长。另外,从用户角度出发,需要将客户端安插至用户主机上,对用户主机的安全性构成威胁。这也是很多用户不愿使用C/S模式应用程序的重要原因。 B/S模式相比C/S模式而言,由于它没有独立的客户端,使用标准浏览器作为客户端,其工作开发量较小。只需开发服务器端即可。另外由于其采用浏览器显示数据,因此移植性非常好,不受平台限制。如早期的偷菜游戏,在各个平台上都可以完美运行。 B/S模式的缺点也较明显。由于使用第三方浏览器,因此网络应用支持受限。另外,没有客户端放到对方主机上,缓存数据不尽如人意,从而传输数据量受到限制。应用的观感大打折扣。第三,必须与浏览器一样,采用标准http协议进行通信,协议选择不灵活。 因此在开发过程中,模式的选择由上述各自的特点决定。根据实际需求选择应用程序设计模式。 简单的C/S模型通信 Server端:Listen函数 func Listen(network, address string) (Listener, error) network:选用的协议:TCP、UDP, 如:“tcp”或 “udp” address:IP地址+端口号, 如:“127.0.0.1:8000”或 “:8000” Listener 接口: type Listener interface { Accept (Conn, error) Close error Addr Addr } Conn 接口: type Conn interface { Read(b byte) (n int, err error) Write(b byte) (n int, err error) Close error LocalAddr Addr RemoteAddr Addr SetDeadline(t time.Time) error SetReadDeadline(t time.Time) error SetWriteDeadline(t time.Time) error } 参看 [<u>https://studygolang.com/pkgdoc</u>](https://studygolang.com/pkgdoc) 中文帮助文档中的demo: 示例代码:TCP服务器.go package main import ( "net" "fmt" ) func main { // 创建监听 listener, err:= net.Listen("tcp", ":8000") if err != nil { fmt.Println("listen err:", err) return } defer listener.Close // 主协程结束时,关闭listener fmt.Println("服务器等待客户端建立连接...") // 等待客户端连接请求 conn, err := listener.Accept if err != nil { fmt.Println("accept err:", err) return } defer conn.Close // 使用结束,断开与客户端链接 fmt.Println("客户端与服务器连接建立成功...") // 接收客户端数据 buf := make(byte, 1024) // 创建1024大小的缓冲区,用于read n, err := conn.Read(buf) if err != nil { fmt.Println("read err:", err) return } fmt.Println("服务器读到:", string(buf[:n])) // 读多少,打印多少。 }
-
用 python 实现前向分区最大匹配算法的示例代码
-
用 python 编写的有趣小程序,简单易懂的 python 小程序