SciPy-秘籍-一-
SciPy 秘籍(一)
零、简介
这是“SciPy 秘籍”——各种用户贡献的秘籍的集合,曾经生活在wiki.scipy.org
之下。请注意,有些是相当古老的(2005-2009 年),今天可能不再适用。如果你想补充/修改,请查看科学秘籍库。
编译扩展
使用 mingw 在 Windows 上编译扩展模块
制图法
输入输出
数据采集用 NIDAQmx 数据采集用 PyUL Fortran I/O 格式 输入输出 LAS 阅读器 从 CCD 相机中读取 SPE 文件 读取 mat 文件hdf 5
Matplotlib / 3D 绘图
【matplotlib VTK 集成】【matplotlib:mplot 3d】
Matplotlib /在应用中嵌入绘图
嵌入 wx【matplot lib:pyside】【matplot lib:滚动打印】
Matplotlib / Misc
加载图像 Matplotlib:调整图像大小 Matplotlib:在 solaris10 上编译 MatplotlibMatplotlib:删除现有数据系列Matplotlib:djangoMatplotlib:交互式绘图Matplotlib:Matplotlib 和 zope
Matplotlib /伪彩色打印
Matplotlib:颜色映射变换 Matplotlib:将矩阵转换为光栅图像 Matplotlib:网格化不规则间隔的数据 Matplotlib:动态加载颜色映射 Matplotlib:绘制具有特殊值的图像 Matplotlib:显示颜色映射
Matplotlib /简单绘图
Matplotlib:动画 Matplotlib:箭头 Matplotlib:条形图 Matplotlib:自定义日志标签 Matplotlib:图表上的提示T21】Matplotlib:图例Matplotlib:地图 Matplotlib:粗轴 Matplotlib:变换 Matplotlib:未填充直方图
Matplotlib /排版
Matplotlib:乳胶示例 Matplotlib:使用特克斯
宫六世
玛雅冲浪【玛雅语】玛雅语:跑步玛雅语 2
**## Mayavi / TVTK
数字与科学/高级主题
Numpy 和 Scipy/插值
Numpy 和 Scipy /线性代数
num py/matplot lib
Numpy 和 Scipy/优化和拟合技术
拟合数据scipy 中的大规模束调整 最小二乘圆 线性回归OLST21】优化和拟合演示T25】优化演示T29】RANSAC
*## 数值积分/常微分方程
耦合弹簧-质量系统 科特威·德·弗里斯方程Matplotlib:lot ka Volterra 教程 建模僵尸启示录 理论生态学:黑斯廷斯和鲍威尔
Numpy 和 Scipy /其他示例
应用 FIR 滤波器 布朗运动 巴特沃斯带通 通信理论 相关随机样本 轻松多线程 眼图FIR 滤波器 Filtfilt 设置点 频率扫描信号 KDTree 示例 卡尔曼滤波 线性分类 粒子滤波 重新绑定T69】Savitzky Golay 滤波
数字和科学/求根
Numpy 和 Scipy /提示和技巧
按名称寻址数组列 构建数组 类卷积运算 索引 numpy 数组 元数组T21】多点 对象数组使用记录数组 为人生游戏跨步招数
其他例子
使用 NumPy 数组的 C 扩展 嵌入特性 GUI 中 Matplotlib:拖放文本示例 Matplotlib:树图 Mayavi:从源代码安装 python 素材T21】Mayavi:示例T25】使用 Pyparsing 读取自定义文本文件
性能
使用 Python 进行性能计算的初学者指南 使用 numpy 和 scipy 进行并行编程
科学图形用户界面
科学脚本
将 NumPy 与其他语言一起使用(高级)
C 扩展CtypesF2py直列编织带基本数组转换(无闪击) Pyrex 和 NumPyT21】SWIG NumPy 示例 SWIG 和 NumPySWIG
过时的
一个数值不可知的 pyrex 类 数组、结构体和 Pyrex 数据框 Python 成像库 时间序列的秘籍T21】fortran file 类dbaseT28xpltT30***
一、编译扩展
使用 mingw 在 Windows 上编译扩展模块
使用 mingw 在 Windows 上编译扩展模块
先决条件
- Python 2.5(使用早期版本的 Python,用 mingw 编译扩展要复杂得多。如果无法升级到 Python 2.5,请使用 visual Studio 2003)
- 您机器上的本地管理员访问权限
编译器设置
-
去 www.mingw.org,去网站的“下载”部分。寻找 sourceforge 页面的链接,然后去那里。获取最新版本的“自动 MinGW 安装程序”并运行安装程序。
-
当被问及“您希望安装哪个 MinGW 软件包”时,根据您是否希望处于领先地位,选择“当前”或“候选”。
-
当您到达需要选择您想要安装的各种组件的部分时,包括“基本工具”和“g++编译器”,如果您要编译一些使用 Fortran 的扩展,可能还包括 g77 编译器。
-
将其安装到“C:”位置(默认位置)
-
如果安装程序没有为您这样做,请将“C:”添加到您的 PATH 环境变量中。如果您正在运行 Vista,您还需要将“C:32.4.5”添加到您的路径中
-
在记事本(或您最喜欢的文本编辑器)中创建新的文本文件,并在文件中输入以下内容:
[build]编译器= mingw32
-
将文件保存为“C:25.cfg”。这将告诉 python 在编译扩展时使用 MinGW 编译器
-
关闭所有打开的命令提示(需要重新打开它们才能看到新的环境变量值)
编译扩展(通过 distutils)
注意:这仅适用于具有使用 distutils 的 setup.py 脚本的模块
- 假设您已经将某个模块的代码下载到文件夹“xyz”中。打开一个命令提示符,然后将光盘放入该目录(例如,键入“C:”然后按 enter 键切换到 c 盘,然后键入“cd c:”切换到目录 xyz)
- 要创建二进制安装程序,请键入“python setup.py bdist_wininst”
- 如果一切正常,那么现在应该有一个名为" dist "的子文件夹,其中包含一个可以运行的 exe 文件来安装模块。
二、图形
线积分卷积
线积分卷积
线积分卷积是一种表示二维矢量场的技术或技术族。其思想是产生在矢量场方向上高度相关但在整个矢量场上不相关的纹理。这是通过生成一个噪声纹理来完成的,然后,对于图像的每个像素,沿着矢量场向前和向后“流动”。沿着这条路径的点在噪声纹理中被查找,并且被平均以给出起点处的 LIC 纹理。基本技术忽略了矢量场的大小和符号。只要稍加修改,同样的技术可以用来沿着矢量场产生“流动”动画。
本页附有 cython 代码,用于实现简单的线积分卷积运算符,外加一些演示 python 代码。演示代码可以或多或少地制作上面的图像——一个简单的漩涡数组;注意一个整体旋转是如何出现在单个涡旋矢量场的总和中的,就像超流体的“整体旋转”实际上是一个涡旋数组一样——或者它可以制作一个相同矢量场的视频。视频处理起来有点笨拙,因为所有标准的视频压缩技术都将其严重破坏,但它确实工作得很好。
附件
宫六世
宫六世
Mayavi2 是一个交互式程序,允许用 Python 和 scipy 精心绘制科学数据的 3D 图。它是三维可视化的玛雅维的继承者。
|| 这一页的信息有点陈旧过时。请参考 Mayavi2 用户指南作为参考。有关脚本的快速介绍,请参见 mlab 。在最近版本的 Mayavi2 中,可以在帮助菜单中访问用户指南,但是最新版本的 Mayavi 的用户指南也可以在线上的找到。||
如果您需要更多帮助,我们将邀请您在entnown-dev邮件列表中提问。
#!figure
#class right
## Snazzy graphics here...
# ![](files/attachments/MayaVi/mayavi2.png
!Mayavi2 relies on [http://www.vtk.org VTK], and especially a python interface to it: [https://svn.enthought.com/enthought/wiki/TVTK TVTK].
A mayavi2 session.
MayaVi2 主题
!MayaVi2 可以作为交互程序使用,也可以不作为交互程序使用,如下所述。
* For installation of !MayaVi2 in the enthought tool suite see the ```pyMayavi``documentation
<http://enthought.github.com/mayavi/mayavi/installation.html>` _ _
* Using !MayaVi2:
There are (at least) two ways to use !MayaVi2:
* [:Cookbook/MayaVi/RunningMayavi2: Running MayaVi2] on the command line.
* [:Cookbook/MayaVi/ScriptingMayavi2: Scripting MayaVi2] in Python.
* [:Cookbook/MayaVi/Examples: Scripting Examples] (all provided in !MayaVi2 svn tree):
* Using Contour Module (contour.py)
* Using Glyph Module (glyph.py)
* Using Mayavi2 without GUI (nongui.py)
* A 3D array as numerical source (numeric_source.py)
* Using Streamline Module (streamline.py)
* Using !ImagePlaneWidget Module (test.py)
* Plotting a surface from a matrix (surf_regular_mlab.py). See also [:Cookbook/MayaVi/Surf: Cookbook/MayaVi/Surf]
* [:Cookbook/MayaVi/Tips: Tips]: General tips for !MayaVi2 and around.
Vtk 体绘制
Vtk 体绘制
由于我在计算如何使用 VTK 来渲染包含在三维 numpy 数组中的数据时遇到了一些问题,所以我决定分享我的代码。这段代码基于 VTK 的优秀文档和大卫·戈比在 http://public.kitware.com/cgi-bin/cvsweb.cgi/vtk/python/?创建的过时的 vtkImageImportFromArray 类 cvsroot=vtk
对于更高级的功能,这个例子非常简单:阅读文档。
import vtk
from numpy import *
# We begin by creating the data we want to render.
# For this tutorial, we create a 3D-image containing three overlaping cubes.
# This data can of course easily be replaced by data from a medical CT-scan or anything else three dimensional.
# The only limit is that the data must be reduced to unsigned 8 bit or 16 bit integers.
data_matrix = zeros([75, 75, 75], dtype=uint8)
data_matrix[0:35, 0:35, 0:35] = 50
data_matrix[25:55, 25:55, 25:55] = 100
data_matrix[45:74, 45:74, 45:74] = 150
# For VTK to be able to use the data, it must be stored as a VTK-image. This can be done by the vtkImageImport-class which
# imports raw data and stores it.
dataImporter = vtk.vtkImageImport()
# The preaviusly created array is converted to a string of chars and imported.
data_string = data_matrix.tostring()
dataImporter.CopyImportVoidPointer(data_string, len(data_string))
# The type of the newly imported data is set to unsigned char (uint8)
dataImporter.SetDataScalarTypeToUnsignedChar()
# Because the data that is imported only contains an intensity value (it isnt RGB-coded or someting similar), the importer
# must be told this is the case.
dataImporter.SetNumberOfScalarComponents(1)
# The following two functions describe how the data is stored and the dimensions of the array it is stored in. For this
# simple case, all axes are of length 75 and begins with the first element. For other data, this is probably not the case.
# I have to admit however, that I honestly dont know the difference between SetDataExtent() and SetWholeExtent() although
# VTK complains if not both are used.
dataImporter.SetDataExtent(0, 74, 0, 74, 0, 74)
dataImporter.SetWholeExtent(0, 74, 0, 74, 0, 74)
# The following class is used to store transparencyv-values for later retrival. In our case, we want the value 0 to be
# completly opaque whereas the three different cubes are given different transperancy-values to show how it works.
alphaChannelFunc = vtk.vtkPiecewiseFunction()
alphaChannelFunc.AddPoint(0, 0.0)
alphaChannelFunc.AddPoint(50, 0.05)
alphaChannelFunc.AddPoint(100, 0.1)
alphaChannelFunc.AddPoint(150, 0.2)
# This class stores color data and can create color tables from a few color points. For this demo, we want the three cubes
# to be of the colors red green and blue.
colorFunc = vtk.vtkColorTransferFunction()
colorFunc.AddRGBPoint(50, 1.0, 0.0, 0.0)
colorFunc.AddRGBPoint(100, 0.0, 1.0, 0.0)
colorFunc.AddRGBPoint(150, 0.0, 0.0, 1.0)
# The preavius two classes stored properties. Because we want to apply these properties to the volume we want to render,
# we have to store them in a class that stores volume prpoperties.
volumeProperty = vtk.vtkVolumeProperty()
volumeProperty.SetColor(colorFunc)
volumeProperty.SetScalarOpacity(alphaChannelFunc)
# This class describes how the volume is rendered (through ray tracing).
compositeFunction = vtk.vtkVolumeRayCastCompositeFunction()
# We can finally create our volume. We also have to specify the data for it, as well as how the data will be rendered.
volumeMapper = vtk.vtkVolumeRayCastMapper()
volumeMapper.SetVolumeRayCastFunction(compositeFunction)
volumeMapper.SetInputConnection(dataImporter.GetOutputPort())
# The class vtkVolume is used to pair the preaviusly declared volume as well as the properties to be used when rendering that volume.
volume = vtk.vtkVolume()
volume.SetMapper(volumeMapper)
volume.SetProperty(volumeProperty)
# With almost everything else ready, its time to initialize the renderer and window, as well as creating a method for exiting the application
renderer = vtk.vtkRenderer()
renderWin = vtk.vtkRenderWindow()
renderWin.AddRenderer(renderer)
renderInteractor = vtk.vtkRenderWindowInteractor()
renderInteractor.SetRenderWindow(renderWin)
# We add the volume to the renderer ...
renderer.AddVolume(volume)
# ... set background color to white ...
renderer.SetBackground(1, 1, 1)
# ... and set window size.
renderWin.SetSize(400, 400)
# A simple function to be called when the user decides to quit the application.
def exitCheck(obj, event):
if obj.GetEventPending() != 0:
obj.SetAbortRender(1)
# Tell the application to use the function as an exit check.
renderWin.AddObserver("AbortCheckEvent", exitCheck)
renderInteractor.Initialize()
# Because nothing will be rendered without any input, we order the first render manually before control is handed over to the main-loop.
renderWin.Render()
renderInteractor.Start()
```py
要退出应用,只需按下 *q* 。
在我看来,如果不使用以下选项,体积渲染器会创建非常难看的图像:
volumeProperty.ShadeOn()
# 输入输出
# 输入输出
* [用 NIDAQmx 采集数据](Data_Acquisition_with_NIDAQmx.html)
* [用 PyUL 采集数据](Data_Acquisition_with_PyUL.html)
* [Fortran 输入/输出格式](FortranIO.html)
* [输入输出](InputOutput.html)
* 读者
* [从 CCD 摄像头读取 SPE 文件](Reading_SPE_files.html)
* [读取 mat 文件](Reading_mat_files.html)
* [Matlab 7.3 及更高版本](Reading_mat_files.html#matlab-7-3-and-greater)
* [Matlab 中的 hdf 5](hdf5_in_Matlab.html)
# 使用 NIDAQmx 进行数据采集
# 使用 NIDAQmx 进行数据采集
这些是使用[国家仪器公司的](http://ni.com) [NI-DAQmx](http://www.ni.com/dataacquisition/nidaqmx.htm) 库使用 [ctypes](http://docs.python.org/lib/module-ctypes.html) 和 numpy 进行数据采集和回放的快速示例。该库允许访问他们的各种数据采集设备。通过使用 ctypes,我们就不需要 C 编译器了。下面的代码假设是一个 Windows 平台。NI-DAQmx 也适用于 Linux,但是下面的代码需要一些小的改动,即加载共享库和设置函数签名。
另请参见`Data acquisition with PyUniversalLibrary`。
另请参见用 Linux 支持包装 NI-DAQmx 库的项目: [pylibnidaqmx](http://code.google.com/p/pylibnidaqmx/) 、 [pydaqmx](http://code.google.com/p/pydaqmx/) 、 [daqmxbase-swig](http://code.google.com/p/daqmxbase-swig/) 。
好了,说够了,让我们看看代码!
## 模拟采集
!python numbers=disable
Acq_IncClk.py
This is a near-verbatim translation of the example program
C:\Program Files\National Instruments\NI-DAQ\Examples\DAQmx ANSI C\Analog In\Measure Voltage\Acq-Int Clk\Acq-IntClk.c
import ctypes
import numpy
nidaq = ctypes.windll.nicaiu # load the DLL
##############################
Setup some typedefs and constants
to correspond with values in
C:\Program Files\National Instruments\NI-DAQ\DAQmx ANSI C Dev\include\NIDAQmx.h
the typedefs
int32 = ctypes.c_long
uInt32 = ctypes.c_ulong
uInt64 = ctypes.c_ulonglong
float64 = ctypes.c_double
TaskHandle = uInt32
the constants
DAQmx_Val_Cfg_Default = int32(-1)
DAQmx_Val_Volts = 10348
DAQmx_Val_Rising = 10280
DAQmx_Val_FiniteSamps = 10178
DAQmx_Val_GroupByChannel = 0
##############################
def CHK(err):
"""a simple error checking routine"""
if err < 0:
buf_size = 100
buf = ctypes.create_string_buffer('\000' * buf_size)
nidaq.DAQmxGetErrorString(err,ctypes.byref(buf),buf_size)
raise RuntimeError('nidaq call failed with error %d: %s'%(err,repr(buf.value)))
initialize variables
taskHandle = TaskHandle(0)
max_num_samples = 1000
data = numpy.zeros((max_num_samples,),dtype=numpy.float64)
now, on with the program
CHK(nidaq.DAQmxCreateTask("",ctypes.byref(taskHandle)))
CHK(nidaq.DAQmxCreateAIVoltageChan(taskHandle,"Dev1/ai0","",
DAQmx_Val_Cfg_Default,
float64(-10.0),float64(10.0),
DAQmx_Val_Volts,None))
CHK(nidaq.DAQmxCfgSampClkTiming(taskHandle,"",float64(10000.0),
DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,
uInt64(max_num_samples)));
CHK(nidaq.DAQmxStartTask(taskHandle))
read = int32()
CHK(nidaq.DAQmxReadAnalogF64(taskHandle,max_num_samples,float64(10.0),
DAQmx_Val_GroupByChannel,data.ctypes.data,
max_num_samples,ctypes.byref(read),None))
print "Acquired %d points"%(read.value)
if taskHandle.value != 0:
nidaq.DAQmxStopTask(taskHandle)
nidaq.DAQmxClearTask(taskHandle)
print "End of program, press Enter key to quit"
raw_input()
## 模拟生成
!python numbers=disable
"""
This is an interpretation of the example program
C:\Program Files\National Instruments\NI-DAQ\Examples\DAQmx ANSI C\Analog Out\Generate Voltage\Cont Gen Volt Wfm-Int Clk\ContGen-IntClk.c
This routine will play an arbitrary-length waveform file.
This module depends on:
numpy
Adapted by Martin Bures [ mbures { @ } zoll { . } com ]
"""
import system libraries
import ctypes
import numpy
import threading
load any DLLs
nidaq = ctypes.windll.nicaiu # load the DLL
##############################
Setup some typedefs and constants
to correspond with values in
C:\Program Files\National Instruments\NI-DAQ\DAQmx ANSI C Dev\include\NIDAQmx.h
the typedefs
int32 = ctypes.c_long
uInt32 = ctypes.c_ulong
uInt64 = ctypes.c_ulonglong
float64 = ctypes.c_double
TaskHandle = uInt32
the constants
DAQmx_Val_Cfg_Default = int32(-1)
DAQmx_Val_Volts = 10348
DAQmx_Val_Rising = 10280
DAQmx_Val_FiniteSamps = 10178
DAQmx_Val_ContSamps = 10123
DAQmx_Val_GroupByChannel = 0
##############################
class WaveformThread( threading.Thread ):
"""
This class performs the necessary initialization of the DAQ hardware and
spawns a thread to handle playback of the signal.
It takes as input arguments the waveform to play and the sample rate at which
to play it.
This will play an arbitrary-length waveform file.
"""
def init( self, waveform, sampleRate ):
self.running = True
self.sampleRate = sampleRate
self.periodLength = len( waveform )
self.taskHandle = TaskHandle( 0 )
self.data = numpy.zeros( ( self.periodLength, ), dtype=numpy.float64 )
# convert waveform to a numpy array
for i in range( self.periodLength ):
self.data[ i ] = waveform[ i ]
# setup the DAQ hardware
self.CHK(nidaq.DAQmxCreateTask("",
ctypes.byref( self.taskHandle )))
self.CHK(nidaq.DAQmxCreateAOVoltageChan( self.taskHandle,
"Dev1/ao0",
"",
float64(-10.0),
float64(10.0),
DAQmx_Val_Volts,
None))
self.CHK(nidaq.DAQmxCfgSampClkTiming( self.taskHandle,
"",
float64(self.sampleRate),
DAQmx_Val_Rising,
DAQmx_Val_FiniteSamps,
uInt64(self.periodLength)));
self.CHK(nidaq.DAQmxWriteAnalogF64( self.taskHandle,
int32(self.periodLength),
0,
float64(-1),
DAQmx_Val_GroupByChannel,
self.data.ctypes.data,
None,
None))
threading.Thread.init( self )
def CHK( self, err ):
"""a simple error checking routine"""
if err < 0:
buf_size = 100
buf = ctypes.create_string_buffer('\000' * buf_size)
nidaq.DAQmxGetErrorString(err,ctypes.byref(buf),buf_size)
raise RuntimeError('nidaq call failed with error %d: %s'%(err,repr(buf.value)))
if err > 0:
buf_size = 100
buf = ctypes.create_string_buffer('\000' * buf_size)
nidaq.DAQmxGetErrorString(err,ctypes.byref(buf),buf_size)
raise RuntimeError('nidaq generated warning %d: %s'%(err,repr(buf.value)))
def run( self ):
counter = 0
self.CHK(nidaq.DAQmxStartTask( self.taskHandle ))
def stop( self ):
self.running = False
nidaq.DAQmxStopTask( self.taskHandle )
nidaq.DAQmxClearTask( self.taskHandle )
if name == 'main':
import time
# generate a time signal 5 seconds long with 250Hz sample rate
t = numpy.arange( 0, 5, 1.0/250.0 )
# generate sine wave
x = sin( t )
mythread = WaveformThread( x, 250 )
# start playing waveform
mythread.start()
# wait 5 seconds then stop
time.sleep( 5 )
mythread.stop()
# 用 PyUL 采集数据
# 用 PyUL 采集数据
## 介绍
本页说明了来自[测量计算](http://www.measurementcomputing.com)的廉价(约 150 美元) [PMD USB-1208FS](http://www.measurementcomputing.com/cbicatalog/cbiproduct_new.asp?dept_id=412&pf_id=1665&mscssid=G9PDTGJV5VES9P694WLRS3JWG3J615M7) 数据采集设备的使用。它利用了[pyuniversallbrary](http://www.its.caltech.edu/~astraw/pyul.html),一个测量计算的[世界图书馆](http://www.measurementcomputing.com/cbicatalog/cbiproduct.asp?dept%5Fid=261&pf%5Fid=1084&mscssid=RDNUK9VN7L3L8PL34QF282AX3F987098)的开源包装器。
另请参见使用 Ni-DAQmx 采集数据。
以下示例是用 PyUL 发行版 20050624 制作的。这个版本的[预编译的 win32 二进制文件](http://www.its.caltech.edu/~astraw/PyUniversalLibrary/PyUniversalLibrary-20050624.win32-py2.4-num23.7.exe)与 Python 2.4 的[entrement 版本(1.0.0 版,2006-08-02 12:20)兼容,这就是运行这些示例的方式。](http://code.enthought.com/enthon/)
## 示例 1 -简单模拟输入
第一个示例说明了无缓冲模拟输入的使用:
example1.py
import UniversalLibrary as UL
import time
BoardNum = 0
Gain = UL.BIP5VOLTS
Chan = 0
tstart = time.time()
data = []
times = []
while 1:
DataValue = UL.cbAIn(BoardNum, Chan, Gain)
data.append( DataValue )
times.append( time.time()-tstart )
if times[-1] > 1.0:
break
import pylab
pylab.plot(times,data,'o-')
pylab.xlabel('time (sec)')
pylab.ylabel('ADC units')
pylab.show()
当我运行这个程序时,有一个函数发生器产生一个正弦波,连接到我的设备的引脚 1 和 2。这将产生如下图:
![](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/8a629bbb.jpg)
## 示例 2 -获取伏特而不是任意单位
示例 1 中记录的值是“模数转换器单位”,即由模数硬件直接记录的值。事实上,该器件有一个 12 位模数转换器,但数值存储为 16 位有符号整数。为了将这些值转换成伏特,我们使用测量计算的功能。在这里,我们对每一条数据都这样做,并绘制结果。
example2.py
import UniversalLibrary as UL
import time
BoardNum = 0
Gain = UL.BIP5VOLTS
Chan = 0
tstart = time.time()
data = []
times = []
while 1:
DataValue = UL.cbAIn(BoardNum, Chan, Gain)
EngUnits = UL.cbToEngUnits(BoardNum, Gain, DataValue)
data.append( EngUnits )
times.append( time.time()-tstart )
if times[-1] > 1.0:
break
import pylab
pylab.plot(times,data,'o-')
pylab.xlabel('time (sec)')
pylab.ylabel('Volts')
pylab.savefig('example2.png',dpi=72)
pylab.show()
现在输出值以伏特为单位:
![](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/cdc2e16b.jpg)
## 示例 3 -缓冲输入
正如你无疑已经注意到的,上面的图不是非常“纯”的正弦波。这无疑是由于我们采样数据的方式。我们不是依靠稳定的时钟来完成收购,而是以设备(和操作系统)允许的速度轮询设备。还有一个更好的方法——我们可以使用测量计算设备上的时钟,以均匀间隔的样本获取数据缓冲区。
example3.py
import UniversalLibrary as UL
import Numeric
import pylab
BoardNum = 0
Gain = UL.BIP5VOLTS
LowChan = 0
HighChan = 0
Count = 2000
Rate = 3125
Options = UL.CONVERTDATA
ADData = Numeric.zeros((Count,), Numeric.Int16)
ActualRate = UL.cbAInScan(BoardNum, LowChan, HighChan, Count,
Rate, Gain, ADData, Options)
convert to Volts
data_in_volts = [ UL.cbToEngUnits(BoardNum, Gain, y) for y in ADData]
time = Numeric.arange( ADData.shape[0] )*1.0/ActualRate
pylab.plot(time, data_in_volts, 'o-')
pylab.xlabel('time (sec)')
pylab.ylabel('Volts')
pylab.savefig('example3.png',dpi=72)
pylab.show()
输出看起来好多了:![image0](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/f0a2c8db.jpg)
## 示例 4 -计算功率谱
现在我们可以使用 py lab(matplotlib 的一部分)的函数来计算功率谱密度。
example4.py
import UniversalLibrary as UL
import Numeric
import pylab
BoardNum = 0
Gain = UL.BIP5VOLTS
LowChan = 0
HighChan = 0
Count = 2000
Rate = 10000
Options = UL.CONVERTDATA
ADData = Numeric.zeros((Count,), Numeric.Int16)
ActualRate = UL.cbAInScan(BoardNum, LowChan, HighChan, Count,
Rate, Gain, ADData, Options)
time = Numeric.arange( ADData.shape[0] )*1.0/ActualRate
convert to Volts
data_in_volts = [ UL.cbToEngUnits(BoardNum, Gain, y) for y in ADData]
data_in_volts = Numeric.array(data_in_volts) # convert to Numeric array
pxx, freqs = pylab.psd( data_in_volts, Fs=ActualRate )
decibels = 10*Numeric.log10(pxx)
pylab.subplot(2,1,1)
pylab.plot(time[100:200],data_in_volts[100:200],'o-') # plot a few samples
pylab.xlabel('time (sec)')
pylab.ylabel('Volts')
pylab.subplot(2,1,2)
pylab.plot(freqs, decibels, 'o-')
pylab.xlabel('frequency')
pylab.ylabel('Power (decibels)')
pylab.savefig('example4.png',dpi=72)
pylab.show()
在这个例子中,我把函数发生器的频率调到了 480 赫兹。你可以看到,这就是`psd()`函数告诉我们的:
![](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/428214cb.jpg)
## 附件
* [`example1.png`](../_downloads/example1.jpg)
* [`example2.png`](../_downloads/example2.jpg)
* [`example3.png`](../_downloads/example3.jpg)
* [`example4.png`](../_downloads/example4.jpg)
![http://scipy-cookbook.readthedocs.org/_img/example1.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/8a629bbb.jpg) ![http://scipy-cookbook.readthedocs.org/_img/example2.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/cdc2e16b.jpg) ![http://scipy-cookbook.readthedocs.org/_img/example3.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/f0a2c8db.jpg) ![http://scipy-cookbook.readthedocs.org/_img/example4.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/428214cb.jpg)
# Fortran 输入输出格式
# Fortran 输入输出格式
> **注意**:你可能想用 [scipy.io.FortranFile](http://docs.scipy.org/doc/scipy/reference/generated/scipy.io.FortranFile.html) 代替。
由 Fortran 程序编写的文件可以使用两种格式之一编写:格式化的或未格式化的。格式化文件是以人类可读的格式编写的,应该可以使用 numpy.fromfile 加载它们。未格式化文件是使用 Fortran 标准未指定的二进制格式编写的。实际上,大多数编译器/运行时使用基于记录的格式,其整数头由以字节为单位的记录长度组成,然后是记录本身,其后是以字节为单位的整数尾。
假设头和数据的精度和端序是未指定的,那么在野外可能会看到大量可能的组合。 [*FortranFile*](FortranIO_FortranFile.html) 类可以处理其中的许多。
以下是如何读取特定未格式化输出文件的示例。请注意代表页眉和页脚的数据类型的“i4”元素的存在。
## 读取 FORTRAN“无格式输入输出”文件
许多科学代码都是用 FORTRAN 编写的。在 FORTRAN 中创建的最方便的文件格式之一是所谓的“[无格式二进制文件](http://local.wasp.uwa.edu.au/~pbourke/dataformats/fortran/)”。这些文件具有原始二进制 IO 的所有缺点-没有元数据,数据依赖于主机端序、浮点表示以及可能的字长-但不仅仅是原始二进制。它们被组织成“记录”,用大小信息填充。然而,人们确实不时会遇到这样的文件。在 numpy/scipy 中似乎没有预写的代码可以用来读取它们,但是使用 numpy 的记录数组可以相对容易地完成:
A = N.fromfile("/tmp/tmp_i7j_a/resid2.tmp",
... N.dtype([('pad1','i4'),
... ('TOA','f8'),
... ('resid_p','f8'),
... ('resid_s','f8'),
... ('orb_p','f8'),
... ('f','f8'),
... ('wt','f8'),
... ('sig','f8'),
... ('preres_s','f8'),
... ('pad3','i8'),
... ('pad2','i4')]))
此示例旨在读取由[[http://www.atnf.csiro.au/research/pulsar/tempo/](http://www.atnf.csiro.au/research/pulsar/tempo/)TEMPO 输出的[[http://www . atnf . CSIRO . au/research/pulsar/TEMPO/ref _ man _ sections/output . txt](http://www.atnf.csiro.au/research/pulsar/tempo/ref_man_sections/output.txt)a file]。大多数字段,“TOA”到“preres_s”都是文件中存在且感兴趣的字段。字段“pad3”要么是文件格式的未记录的增加,要么是某种填充(在我的测试文件中总是零)。FORTRAN 无格式输入输出增加了字段“pad1”和“pad2”。每个都应该包含每个记录的长度(以字节为单位)(因此可以推断出额外的“pad3”字段的存在)。这段代码忽略 t
# 三、输入和输出
## 介绍
本页举例说明如何读或写!NumPy 数组,无论是 ascii 还是二进制。所展示的各种方法都有丰富的、有时是复杂的选项,请致电帮助获取详细信息。
我们将考虑一个简单的例子,我们创建一个名为`data`的零数组,将其写入一个文件`myfile.txt` (myfile.dat 代表二进制情况),并将其读入`read_data`。
可以通过讨论更复杂的情况(例如多个数组)和讨论所提出的各种方法的成本/收益来改进该文档。
## 文本文件
### 我的天啊
写文件可以使用`savetxt`完成。到目前为止,读取文本数据最简单的方法是通过 genfromtxt(或派生的便利函数 recfromtxt 和 recfromcsv)。
```py
>>> from numpy import *
>>> data = zeros((3,3))
>>>#Write data:
>>> savetxt("myfile.txt", data)
>>>#Read:
>>> data = genfromtxt("myfile.txt") }}}
== Matplotlib (pylab) ==
Matplotlib provides an easy solution which seems to load data faster than read_array:
{{{#!python numbers=disable
>>> from numpy import *
>>> from pylab import load # warning, the load() function of numpy will be shadowed
>>> from pylab import save
>>> data = zeros((3,3))
>>> save('myfile.txt', data)
>>> read_data = load("myfile.txt")
numPy
>>> savetxt('myfile.txt', data, fmt="%12.6G") # save to file
>>> from numpy import *
>>> data = genfromtxt('table.dat', unpack=True)
csv 文件
请注意,csv 代表“逗号分隔值”。这意味着分隔符(也称为分隔符),即用于分隔文件中各个值的字符,是逗号。在上面的例子中,默认的分隔符是一个空格,但是上面所有的方法都有一个选项(详情参见它们各自的帮助),可以设置为逗号,以便读取或写入 csv 文件。
一个更复杂的例子
或者,假设您已经将 numpy 作为 N 导入,您可能想要读取任意的列类型。你也可以返回一个重数组,让你给你的数组分配“列标题”。
def read_array(filename, dtype, separator=','):
""" Read a file with an arbitrary number of columns.
The type of data in each column is arbitrary
It will be cast to the given dtype at runtime
"""
cast = N.cast
data = [[] for dummy in xrange(len(dtype))]
for line in open(filename, 'r'):
fields = line.strip().split(separator)
for i, number in enumerate(fields):
data[i].append(number)
for i in xrange(len(dtype)):
data[i] = cast[dtype[i]](data[i])
return N.rec.array(data, dtype=dtype)
然后可以用相应的数据类型调用它:
mydescr = N.dtype([('column1', 'int32'), ('column2Name', 'uint32'), ('col3', 'uint64'), ('c4', 'float32')])
myrecarray = read_array('file.csv', mydescr)
二进制文件
二进制文件的优点是文件大小大大减小。付出的代价是失去了人的可读性,在某些格式中,失去了可移植性。
让我们考虑前面例子中的数组。
带有元数据的文件格式
最简单的可能是使用自己的二进制文件格式。看吧。
>>> numpy.save('test.npy', data)
>>> data2 = numpy.load('test.npy')
您可以使用将多个数组保存在一个文件中。加载文件时,您会得到一个类型为的对象。您可以获得数组列表,并像这样加载单个数组:
>>> numpy.savez('foo.npz', a=a,b=b)
>>> foo = numpy.load('foo.npz')
>>> foo.files
['a', 'b']
>>> a2 = foo['a']
>>> b2 = foo['b']
在旧系统上,标准是使用 python 的 pickle 模块来酸洗数组。
原始二进制
这些文件格式只是写出了数组的内部表示。这依赖于平台,不包含关于数组形状或数据类型的信息,但是快速简单。
SciPy 提供了来自 SciPy . io . numpiyo 的 fwrite()。您必须设置数据的大小,并可选地设置其类型(整数、短整型、浮点型等;见 1 )。
为了读取二进制文件,scipy . io . numpiyo 提供了 fread()。你必须知道数组的数据类型、大小和形状。
>>> from scipy.io.numpyio import fwrite, fread
>>> data = zeros((3,3))
>>>#write: fd = open('myfile.dat', 'wb')
>>> fwrite(fd, data.size, data)
>>> fd.close()
>>>#read:
>>> fd = open('myfile.dat', 'rb')
>>> datatype = 'i'
>>> size = 9
>>> shape = (3,3)
>>> read_data = fread(fd, size, datatype)
>>> read_data = data.reshape(shape)
或者,您可以简单地使用和。遵循前面的示例:
>>> data.tofile('myfile.dat')
>>> fd = open('myfile.dat', 'rb')
>>> read_data = numpy.fromfile(file=fd, dtype=numpy.uint8).reshape(shape)
numpy data type. The option {{{fromfile(..., count=<number>)}}} specifies the number of data entries of that type you want to read in (the default -1 means read in the whole file, which is what you usually want). However, the method is not recommended for data storage and transfer between different platforms, since no byteorder and datatype information is stored (see also the docstrings).
If you want that, use {{{numpy}}}'s own binary file format. See {{{numpy.save}}}, {{{numpy.savez}}} and {{{numpy.load}}}.
{{{#! python numbers=disable
>>> numpy.save('test.npy', data)
>>> data2 = numpy.load('test.npy')
另一种完全控制写入和回读的 rank > 1 数组和数据类型的字节顺序(byteorder)、存储顺序(row-major、column-major)的方法是,但不推荐使用。写作:
>>> from scipy.io import npfile
>>> shape = (3,3)
>>> data = numpy.random.random(shape)
>>> npf = npfile('test.dat', order='F', endian='<', permission='wb')
>>> npf.write_array(data)
>>> npf.close()
回读:
>>> npf = npfile('test.dat', order='F', endian='<', permission='rb')
>>> data2 = npf.read_array(float, shape=shape)
>>> npf.close()
将 Fortran 或 C 数组写入带有元数据的二进制文件
libnpy 是一个小型库,提供了使用 NumPy 自己的二进制格式将 C 或 Fortran 数组保存到数据文件的简单例程。有关此格式的描述,请执行以下操作
>>> from numpy.lib import format
>>> help(format)
下面是一个最小的 C 例子cex.c
:
#include"npy.h"
int main(){
double a[2][4] = { { 1, 2, 3, 4 },
{ 5, 6, 7, 8 } };
int shape[2] = { 2, 4 }, fortran_order = 0;
npy_save_double("ca.npy", fortran_order, 2, shape, &a[0][0]);
return 0;
}
程序创建一个文件ca.npy
,你可以用通常的方式加载到 python 中。
>>> ca = np.load('ca.npy')
>>> print ca
[[ 1\. 2\. 3\. 4.]
[ 5\. 6\. 7\. 8.]]
对应的 Fortran 程序fex.f95
,看起来像
program fex
use fnpy
use iso_c_binding
implicit none
integer :: i
real(C_DOUBLE) :: a(2,4) = reshape([(i, i=1,8)], [2,4])
call save_double("fa.npy", shape(a), a)
end program fex
但是 NumPy 数组的条目现在遵循 Fortran(列-主)顺序。
>>> fa = np.load('fa.npy')
>>> print fa
[[ 1\. 3\. 5\. 7.]
[ 2\. 4\. 6\. 8.]]
源代码发行版中的README
文件解释了如何使用make
编译库。
如果您将npy.h
和libnpy.a
放在与cex.c
相同的目录中,那么您可以使用命令构建可执行文件cex
gcc -o cex cex.c libnpy.a
同样,将npy.mod
和libnpy.a
放在与fex.f95
相同的目录中,使用命令构建fex
gfortran -o fex fex.f95 libnpy.a
读者群
读者群
本秘籍示例包含一个模块,该模块实现了 LAS (Log ASCII Standard)测井文件(LAS 2.0)的读取器。更多信息见加拿大测井协会页面关于该格式。
#!python
"""LAS File Reader
The main class defined here is LASReader, a class that reads a LAS file
and makes the data available as a Python object.
"""
# Copyright (c) 2011, Warren Weckesser
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import re
import keyword
import numpy as np
def isidentifier(s):
if s in keyword.kwlist:
return False
return re.match(r'^[a-z_][a-z0-9_]*$', s, re.I) is not None
def _convert_to_value(s):
try:
value = int(s)
except ValueError:
try:
value = float(s)
except ValueError:
value = s
return value
class LASError(Exception):
pass
class LASItem(object):
"""This class is just a namespace, holding the attributes 'name',
'units', 'data', 'value', and 'descr'. 'value' is the numerical
value of 'data', if it has a numerical value (specifically, if
int() or float() don't raise an exception when given the value
of the 'data' attribute).
A class method, from_line(cls, line), is provided to parse
a line from a LAS file and create a LASItem instance.
"""
def __init__(self, name, units='', data='', descr=''):
self.name = name
self.units = units
self.data = data
self.value = _convert_to_value(data)
self.descr = descr
def __str__(self):
s = ("name='%s', units='%s', data='%s', descr='%s'" %
(self.name, self.units, self.data, self.descr))
return s
def __repr__(self):
s = str(self)
return "LASItem(%s)" % s
@classmethod
def from_line(cls, line):
first, descr = line.rsplit(':', 1)
descr = descr.strip()
name, mid = first.split('.', 1)
name = name.strip()
if mid.startswith(' '):
# No units
units = ''
data = mid
else:
units_data = mid.split(None, 1)
if len(units_data) == 1:
units = units_data[0]
data = ''
else:
units, data = units_data
return LASItem(name=name, units=units, data=data.strip(),
descr=descr.strip())
def _read_wrapped_row(f, n):
"""Read a "row" of data from the Ascii section of a "wrapped" LAS file.
`f` must be a file object opened for reading.
`n` is the number of fields in the row.
Returns the list of floats read from the file.
"""
depth = float(f.readline().strip())
values = [depth]
while len(values) < n:
new_values = [float(s) for s in f.readline().split()]
values.extend(new_values)
return values
def _read_wrapped_data(f, dt):
data = []
ncols = len(dt.names)
while True:
try:
row = _read_wrapped_row(f, ncols)
except Exception:
break
data.append(tuple(row))
data = np.array(data, dtype=dt)
return data
class LASSection(object):
"""Represents a "section" of a LAS file.
A section is basically a collection of items, where each item has the
attributes 'name', 'units', 'data' and 'descr'.
Any item in the section whose name is a valid Python identifier is
also attached to the object as an attribute. For example, if `s` is a
LASSection instance, and the corresponding section in the LAS file
contained this line:
FD .K/M3 999.9999 : Fluid Density
then the item may be referred to as `s.FD` (in addition to the longer
`s.items['FD']`).
Attributes
----------
items : dict
The keys are the item names, and the values are LASItem instances.
names : list
List of item names, in the order they were read from the LAS file.
"""
def __init__(self):
# Note: In Python 2.7, 'items' could be an OrderedDict, and
# then 'names' would not be necessary--one could use items.keys().
self.items = dict()
self.names = []
def add_item(self, item):
self.items[item.name] = item
self.names.append(item.name)
if isidentifier(item.name) and not hasattr(self, item.name):
setattr(self, item.name, item)
def display(self):
for name in self.names:
item = self.items[name]
namestr = name
if item.units != '':
namestr = namestr + (" (%s)" % item.units)
print "%-16s %-30s [%s]" % (namestr, "'" + item.data + "'",
item.descr)
class LASReader(object):
"""The LASReader class holds data from a LAS file.
This reader only handles LAS 2.0 files (as far as I know).
Constructor
-----------
LASReader(f, null_subs=None)
f : file object or string
If f is a file object, it must be opened for reading.
If f is a string, it must be the filename of a LAS file.
In that case, the file will be opened and read.
Attributes for LAS Sections
---------------------------
version : LASSection instance
This LASSection holds the items from the '~V' section.
well : LASSection instance
This LASSection holds the items from the '~W' section.
curves : LASection instance
This LASSection holds the items from the '~C' section.
parameters : LASSection instance
This LASSection holds the items from the '~P' section.
other : str
Holds the contents of the '~O' section as a single string.
data : numpy 1D structured array
The numerical data from the '~A' section. The data type
of the array is constructed from the items in the '~C'
section.
Other attributes
----------------
data2d : numpy 2D array of floats
The numerical data from the '~A' section, as a 2D array.
This is a view of the same data as in the `data` attribute.
wrap : bool
True if the LAS file was wrapped. (More specifically, this
attribute is True if the data field of the item with the
name 'WRAP' in the '~V' section has the value 'YES'.)
vers : str
The LAS version. (More specifically, the value of the data
field of the item with the name 'VERS' in the '~V' section).
null : float or None
The numerical value of the 'NULL' item in the '~W' section.
The value will be None if the 'NULL' item was missing.
null_subs : float or None
The value given in the constructor, to be used as the
replacement value of each occurrence of `null_value` in
the log data. The value will be None (and no substitution
will be done) if the `null_subs` argument is not given to
the constructor.
start : float, or None
Numerical value of the 'STRT' item from the '~W' section.
The value will be None if 'STRT' was not given in the file.
start_units : str
Units of the 'STRT' item from the '~W' section.
The value will be None if 'STRT' was not given in the file.
stop : float
Numerical value of the 'STOP' item from the '~W' section.
The value will be None if 'STOP' was not given in the file.
stop_units : str
Units of the 'STOP' item from the '~W' section.
The value will be None if 'STOP' was not given in the file.
step : float
Numerical value of the 'STEP' item from the '~W' section.
The value will be None if 'STEP' was not given in the file.
step_units : str
Units of the 'STEP' item from the '~W' section.
The value will be None if 'STEP' was not given in the file.
"""
def __init__(self, f, null_subs=None):
"""f can be a filename (str) or a file object.
If 'null_subs' is not None, its value replaces any values in the data
that matches the NULL value specified in the Version section of the LAS
file.
"""
self.null = None
self.null_subs = null_subs
self.start = None
self.start_units = None
self.stop = None
self.stop_units = None
self.step = None
self.step_units = None
self.version = LASSection()
self.well = LASSection()
self.curves = LASSection()
self.parameters = LASSection()
self.other = ''
self.data = None
self._read_las(f)
self.data2d = self.data.view(float).reshape(-1, len(self.curves.items))
if null_subs is not None:
self.data2d[self.data2d == self.null] = null_subs
def _read_las(self, f):
"""Read a LAS file.
Returns a dictionary with keys 'V', 'W', 'C', 'P', 'O' and 'A',
corresponding to the sections of a LAS file. The values associated
with keys 'V', 'W', 'C' and 'P' will be lists of Item instances. The
value associated with the 'O' key is a list of strings. The value
associated with the 'A' key is a numpy structured array containing the
log data. The field names of the array are the mnemonics from the
Curve section of the file.
"""
opened_here = False
if isinstance(f, basestring):
opened_here = True
f = open(f, 'r')
self.wrap = False
line = f.readline()
current_section = None
current_section_label = ''
while not line.startswith('~A'):
if not line.startswith('#'):
if line.startswith('~'):
if len(line) < 2:
raise LASError("Missing section character after '~'.")
current_section_label = line[1:2]
other = False
if current_section_label == 'V':
current_section = self.version
elif current_section_label == 'W':
current_section = self.well
elif current_section_label == 'C':
current_section = self.curves
elif current_section_label == 'P':
current_section = self.parameters
elif current_section_label == 'O':
current_section = self.other
other = True
else:
raise LASError("Unknown section '%s'" % line)
elif current_section is None:
raise LASError("Missing first section.")
else:
if other:
# The 'Other' section is just lines of text, so we
# assemble them into a single string.
self.other += line
current_section = self.other
else:
# Parse the line into a LASItem and add it to the
# current section.
m = LASItem.from_line(line)
current_section.add_item(m)
# Check for the required items whose values we'll
# store as attributes of the LASReader instance.
if current_section == self.version:
if m.name == 'WRAP':
if m.data.strip() == 'YES':
self.wrap = True
if m.name == 'VERS':
self.vers = m.data.strip()
if current_section == self.well:
if m.name == 'NULL':
self.null = float(m.data)
elif m.name == 'STRT':
self.start = float(m.data)
self.start_units = m.units
elif m.name == 'STOP':
self.stop = float(m.data)
self.stop_units = m.units
elif m.name == 'STEP':
self.step = float(m.data)
self.step_units = m.units
line = f.readline()
# Finished reading the header--all that is left is the numerical
# data that follows the '~A' line. We'll construct a structured
# data type, and, if the data is not wrapped, use numpy.loadtext
# to read the data into an array. For wrapped rows, we use the
# function _read_wrapped() defined elsewhere in this module.
# The data type is determined by the items from the '~Curves' section.
dt = np.dtype([(name, float) for name in self.curves.names])
if self.wrap:
a = _read_wrapped_data(f, dt)
else:
a = np.loadtxt(f, dtype=dt)
self.data = a
if opened_here:
f.close()
if __name__ == "__main__":
import sys
las = LASReader(sys.argv[1], null_subs=np.nan)
print "wrap? ", las.wrap
print "vers? ", las.vers
print "null =", las.null
print "start =", las.start
print "stop =", las.stop
print "step =", las.step
print "Version ---"
las.version.display()
print "Well ---"
las.well.display()
print "Curves ---"
las.curves.display()
print "Parameters ---"
las.parameters.display()
print "Other ---"
print las.other
print "Data ---"
print las.data2d
源代码: las.py
以下是使用本模块的示例:
>>> import numpy as np
>>> from las import LASReader
>>> sample3 = LASReader('sample3.las', null_subs=np.nan)
>>> print sample3.null
-999.25
>>> print sample3.start, sample3.stop, sample3.step
910.0 909.5 -0.125
>>> print sample3.well.PROV.data, sample3.well.UWI.data
ALBERTA 100123401234W500
>>> from matplotlib.pyplot import plot, show
>>> plot(sample3.data['DEPT'], sample3.data['PHIE'])
[<matplotlib.lines.Line2D object at 0x4c2ae90>]
>>> show()
它创建了以下图:
LAS 文件示例如下:
附件
从 CCD 摄像机读取固相萃取文件
从 CCD 摄像机读取固相萃取文件
有些电荷耦合器件(CCD) 相机(普林斯顿之类的)会产生 SPE 文件。本页建议如何用 Numpy 读取这样的二进制文件,但是代码并不健壮。下面的代码只能读取与示例相同格式的文件,“lampe_dt.spe”(不幸的是维基上唯一的 spe 文件)。
用 numpy 加载固相萃取文件
加载 SPE 文件只需要 Numpy,结果将是一个由颜色组成的数组。图像大小在位置 42 和 656,数据在 4100。SPE 文件头中还有许多其他数据,其中一个必须是数据类型(如果您知道在哪里,欢迎您编辑此页面)。最后请注意,图像总是由 16 位无符号整数编码的颜色组成,但在您的输入文件中可能不是这样。
#!python numbers=disabled
# read_spe.py
import numpy as N
class File(object):
def __init__(self, fname):
self._fid = open(fname, 'rb')
self._load_size()
def _load_size(self):
self._xdim = N.int64(self.read_at(42, 1, N.int16)[0])
self._ydim = N.int64(self.read_at(656, 1, N.int16)[0])
def _load_date_time(self):
rawdate = self.read_at(20, 9, N.int8)
rawtime = self.read_at(172, 6, N.int8)
strdate = ''
for ch in rawdate :
strdate += chr(ch)
for ch in rawtime:
strdate += chr(ch)
self._date_time = time.strptime(strdate,"%d%b%Y%H%M%S")
def get_size(self):
return (self._xdim, self._ydim)
def read_at(self, pos, size, ntype):
self._fid.seek(pos)
return N.fromfile(self._fid, ntype, size)
def load_img(self):
img = self.read_at(4100, self._xdim * self._ydim, N.uint16)
return img.reshape((self._ydim, self._xdim))
def close(self):
self._fid.close()
def load(fname):
fid = File(fname)
img = fid.load_img()
fid.close()
return img
if __name__ == "__main__":
import sys
img = load(sys.argv[-1])
使用 matplotlib 和 ipython 查看图像
档案 read_spe.zip
中提供了上面的“read_spe.py”脚本和“lampe_dt.spe”示例。一旦反编译完毕,#,您就可以在脚本所在的目录中启动 ipython:
ipython -pylab read_spe.py lampe_dt.spe
下面的第一行将在新窗口中显示图像。第二行将更改颜色映射(请尝试“帮助(pylab.colormaps)”来列出它们)。
#!python
>>> pylab.imshow(img)
>>> pylab.hot()
附件
阅读 mat 文件
阅读 mat 文件
Here are exemples of how to read two variables {{{lat}}} and {{{lon}}} from a mat file called "test.mat".
= Matlab up to 7.1 =
mat files created with Matlab up to version 7.1 can be read using the {{{mio}}} module part of {{{scipy.io}}}. Reading structures (and arrays of structures) is supported, elements are accessed with the same syntax as in Matlab: after reading a structure called e.g. {{{struct}}}, its {{{lat}}} element can be obtained with {{{struct.lat}}}, or {{{struct.__getattribute__('lat')}}} if the element name comes from a string.
{{{
#!python
#!/usr/bin/env python
from scipy.io import loadmat
x = loadmat('test.mat')
lon = x['lon']
lat = x['lat']
# one-liner to read a single variable
lon = loadmat('test.mat')['lon']
Matlab 中的 hdf5
Matlab 中的 hdf5
Python 可以以 hdf5 格式保存丰富的分层数据集。Matlab 可以读取 hdf5,但 api 太重,几乎无法使用。下面是一些 matlab 脚本(由 Gail Varoquaux 编写),用于在 matlab 下以 hdf5 格式加载和保存数据,签名与标准的 Matlab 加载/保存功能相同。
这些 Matlab 脚本无法加载 hdf5 中允许的所有类型。请随意提供 python 脚本来使用 pytables 实现与此 hdf5 子集兼容的简单加载/保存功能。
一个注意事项:这些脚本使用“Workspace”命名空间来存储一些变量,它们会在从 Matlab 保存数据时污染您的工作空间。没有什么是我不能接受的。
另一个加载器脚本
下面是第二个 HDF5 加载器脚本,它将数据从一个 HDF5 文件加载到一个 Matlab 结构中
通过仅使用低级别的 HDF5 API,它可以处理比 Matlab 高级功能更多样的 HDF5 数据集(至少 R2008a hdf5info 在分块压缩数据集上失败)。
该脚本还可以识别 Pytables 格式的复数,并根据文件中的逻辑顺序(即。来匹配 Python。默认情况下,内置的 Matlab 函数以相反的顺序返回数据,因此 Python 中的第一维将是 Matlab 中的最后一维)。
附件
四、Matplotlib / 3D 绘图
Matplotlib VTK 集成
Matplotlib VTK 集成
万一你想把 matplotlib 图合并到你的 vtk 应用中,vtk 提供了一个非常简单的方法来导入它们。
下面是一个完整的例子:
from vtk import *
import matplotlib
matplotlib.use('Agg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg
import pylab as p
# The vtkImageImporter will treat a python string as a void pointer
importer = vtkImageImport()
importer.SetDataScalarTypeToUnsignedChar()
importer.SetNumberOfScalarComponents(4)
# It's upside-down when loaded, so add a flip filter
imflip = vtkImageFlip()
imflip.SetInput(importer.GetOutput())
imflip.SetFilteredAxis(1)
# Map the plot as a texture on a cube
cube = vtkCubeSource()
cubeMapper = vtkPolyDataMapper()
cubeMapper.SetInput(cube.GetOutput())
cubeActor = vtkActor()
cubeActor.SetMapper(cubeMapper)
# Create a texture based off of the image
cubeTexture = vtkTexture()
cubeTexture.InterpolateOn()
cubeTexture.SetInput(imflip.GetOutput())
cubeActor.SetTexture(cubeTexture)
ren = vtkRenderer()
ren.AddActor(cubeActor)
renWin = vtkRenderWindow()
renWin.AddRenderer(ren)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
# Now create our plot
fig = Figure()
canvas = FigureCanvasAgg(fig)
ax = fig.add_subplot(111)
ax.grid(True)
ax.set_xlabel('Hello from VTK!', size=16)
ax.bar(xrange(10), p.rand(10))
# Powers of 2 image to be clean
w,h = 1024, 1024
dpi = canvas.figure.get_dpi()
fig.set_figsize_inches(w / dpi, h / dpi)
canvas.draw() # force a draw
# This is where we tell the image importer about the mpl image
extent = (0, w - 1, 0, h - 1, 0, 0)
importer.SetWholeExtent(extent)
importer.SetDataExtent(extent)
importer.SetImportVoidPointer(canvas.buffer_rgba(0,0), 1)
importer.Update()
iren.Initialize()
iren.Start()
让情节成为一个广告牌:
bbmap = vtkImageMapper()
bbmap.SetColorWindow(255.5)
bbmap.SetColorLevel(127.5)
bbmap.SetInput(imflip.GetOutput())
bbact = vtkActor2D()
bbact.SetMapper(hmap)
评论
From zunzun Fri Aug 19 07:06:44 -0500 2005
From: zunzun
Date: Fri, 19 Aug 2005 07:06:44 -0500
Subject:
Message-ID: <20050819070644-0500@www.scipy.org>
from http://sourceforge.net/mailarchive/forum.php?thread_id=7884469&forum_id=33405
If pylab is imported before vtk, everything works fine:
import pylab, vtkpython
pylab.ylabel("Frequency\n", multialignment="center", rotation=90)
n, bins, patches = pylab.hist([1,1,1,2,2,3,4,5,5,5,8,8,8,8], 5)
pylab.show()
If however vtk is imported first:
import vtkpython, pylab
pylab.ylabel("Frequency\n", multialignment="center", rotation=90)
n, bins, patches = pylab.hist([1,1,1,2,2,3,4,5,5,5,8,8,8,8], 5)
pylab.show()
then the Y axis label is positioned incorrectly on the plots.
From earthman Tue Oct 25 15:21:14 -0500 2005
From: earthman
Date: Tue, 25 Oct 2005 15:21:14 -0500
Subject:
Message-ID: <20051025152114-0500@www.scipy.org>
The reason for this is that vtk comes with it's own freetype library, and this is the one being used if vtk is loaded first. Worse symptoms could be errors about fonts not being found. This is typically solved by importing vtk after other packages which might use freetype (pylab, wxPython, etc).
From mroublic Tue Jan 10 11:26:45 -0600 2006
From: mroublic
Date: Tue, 10 Jan 2006 11:26:45 -0600
Subject: One more change I had to make
Message-ID: <20060110112645-0600@www.scipy.org>
In-reply-to: <20050819070644-0500@www.scipy.org>
When I first tried this, I had the error:
Traceback (most recent call last):
File "MatplotlibToVTK.py", line 61, in ?
importer.SetImportVoidPointer(canvas.buffer_rgba(), 1)
TypeError: buffer_rgba() takes exactly 3 arguments (1 given)
I had to add 0,0 to the import line:
importer.SetImportVoidPointer(canvas.buffer_rgba(0,0), 1)
I'm using VTK from CVS using the 5_0 Branch from around November 2005
上面的代码没有在我的系统上运行。我不得不把下面的行:fig.set_figsize_inches(w / dpi,h / dpi)改成:fig . set _ fig size _ inches(1.0 _ w/dpi,1.0_h / dpi)
matplot lib:3d mplot
matplot lib:3d mplot
下面的例子显示了使用 matplotlib 的简单 3D 绘图。matplotlib 的 3D 功能是通过合并约翰·波特的 mplot3d 模块而增加的,因此不再需要额外的下载,以下示例将与最新的 matplotlib 安装一起运行。''注意,matplotlib-0.98 分支不支持此代码,但是如果您需要此功能,可以使用最新的 0.99 版本或 0.91 维护版本。'或者,Mayavi2 项目提供了一个类似 pylab 的 API,用于广泛的 3D 绘图:http://code . entnought . com/projects/mayavi/docs/development/html/mayavi/mlab . html
请注意,并非本页上的所有示例都是最新的,因此其中一些可能无法正常工作。其他例子见http://matplotlib.sourceforge.net/examples/mplot3d/
三维绘图示例:
#!python
from numpy import *
import pylab as p
#import matplotlib.axes3d as p3
import mpl_toolkits.mplot3d.axes3d as p3
# u and v are parametric variables.
# u is an array from 0 to 2*pi, with 100 elements
u=r_[0:2*pi:100j]
# v is an array from 0 to 2*pi, with 100 elements
v=r_[0:pi:100j]
# x, y, and z are the coordinates of the points for plotting
# each is arranged in a 100x100 array
x=10*outer(cos(u),sin(v))
y=10*outer(sin(u),sin(v))
z=10*outer(ones(size(u)),cos(v))
线框(适用于 0.87.5):
#!python
fig=p.figure()
ax = p3.Axes3D(fig)
ax.plot_wireframe(x,y,z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
p.show()
3D 绘图:
#!python
# this connects each of the points with lines
fig=p.figure()
ax = p3.Axes3D(fig)
# plot3D requires a 1D array for x, y, and z
# ravel() converts the 100x100 array into a 1x10000 array
ax.plot3D(ravel(x),ravel(y),ravel(z))
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
fig.add_axes(ax)
p.show()
散布(工作于 0.87.5,显示一些假象):
#!python
fig=p.figure()
ax = p3.Axes3D(fig)
# scatter3D requires a 1D array for x, y, and z
# ravel() converts the 100x100 array into a 1x10000 array
ax.scatter3D(ravel(x),ravel(y),ravel(z))
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
p.show()
表面(在 0.87.5 上工作):
#!python
fig=p.figure()
ax = p3.Axes3D(fig)
# x, y, and z are 100x100 arrays
ax.plot_surface(x,y,z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
p.show()
Contour3D(适用于 0.87.5):
#!python
delta = 0.025
x = arange(-3.0, 3.0, delta)
y = arange(-2.0, 2.0, delta)
X, Y = p.meshgrid(x, y)
Z1 = p.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = p.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)
fig=p.figure()
ax = p3.Axes3D(fig)
ax.contour3D(X,Y,Z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
p.show()
三维轮廓:
#!python
# in mplt3D change:
# levels, colls = self.contourf(X, Y, Z, 20)
# to:
# C = self.contourf(X, Y, Z, *args, **kwargs)
# levels, colls = (C.levels, C.collections)
fig=p.figure()
ax = p3.Axes3D(fig)
ax.contourf3D(X,Y,Z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
fig.add_axes(ax)
p.show()
2D 等高线图(工作范围为 0.87.5):
#!python
x=r_[-10:10:100j]
y=r_[-10:10:100j]
z= add.outer(x*x, y*y)
### Contour plot of z = x**2 + y**2
p.contour(x,y,z)
### ContourF plot of z = x**2 + y**2
p.figure()
p.contourf(x,y,z)
p.show()
有关 3d 绘图功能的其他一些示例,请运行以下命令。有关更多信息,请参见 matplotlib/axes3d.py 的源代码:
#!python
# note that for the following to work you have to modify the test funcitons in your site-packages/matplotlib/axes3d.py like this:
#def test_xxxx():
# import pylab
# ax = Axes3D(pylab.figure())
# ....
# ....
# pylab.show()
# the following then work on 0.87.5
p3.test_bar2D()
p3.test_contour()
p3.test_scatter()
p3.test_scatter2D()
p3.test_surface()
# the following fail on 0.87.5
p3.test_plot()
p3.test_polys()
p3.test_wir
附件
contour.jpg
contour.png
contour3D.jpg
contour3D.png
contourf.jpg
contourf.png
contourf3D.jpg
contourf3D.png
plot.jpg
plot.png
scatter.jpg
scatter.png
surface.jpg
surface.png
test1.jpg
test1.png
test2.jpg
test2.png
test3.jpg
test3.png
wireframe.jpg
wireframe.png
五、Matplotlib / 在应用中嵌入绘图
嵌入 Wx
嵌入 Wx
Matplotlib 可以嵌入到 wxPython 应用中,以提供高质量的数据可视化。有两种方法,直接嵌入和使用嵌入库。
“直接嵌入”是您放置一个 wxPython 后端小部件(哪个子类{{{wx。Panel}}})直接插入到您的应用中,并使用 matplotlib 的面向对象 API 在其上绘制图形。这种方法通过[http://CVS . SourceForge . net/viewcvs . py/matplotlib/matplotlib/examples/embedding _ in _ wx *得到了证明。matplotlib 附带的示例。无论是{{{FigureCanvasWx}}}还是{{{FigureCanvasWxAgg}}}都没有为用户交互提供任何便利,比如在鼠标下显示坐标,所以你必须自己实现这些东西。matplotlib 示例[http://CVS . SourceForge . net/view CVS . py/% 2 check out % 2A/matplotlib/matplotlib/examples/wxcursor _ demo . py?content-type = text % 2 fplainwxcursor _ demo . py]应该可以帮助您入门。
“嵌入库”通过提供已经支持用户交互和其他花哨功能的绘图小部件,为您节省了大量时间和精力。我知道有两个这样的库:
- Matt Newville 的[http://cars9.uchicago.edu/~newville/Python/MPlot/MPlot]包支持使用 pylab 风格的{{{plot()}}和{{{oplot()}}方法绘制 2D 线图。
- 肯!McIvor 的[http://agni.phys.iit.edu/~kmcivor/wxmpl/WxMpl]模块支持使用 matplotlib 的面向对象 API 绘制所有绘图类型。
这些库中的每一个都有不同的优点和缺点,所以我鼓励您评估它们,并选择最符合您需求的一个。
学习面向对象的 API
如果您要在 wxPython 程序中嵌入 matplotlib,您可能需要在某个时候使用 Matplotlib 的面向对象 API。振作起来,因为它与皮拉布原料药非常匹配,很容易获得。还有更多的细节需要处理,但对于已经在用 wxPython 编程的人来说,这没有问题!;-)
matplotlib 常见问题解答[http://matplotlib.sourceforge.net/faq.html#OO]链接到几个学习面向对象 API 的资源。一旦你涉世未深,阅读课堂文件是最有用的信息来源。[http://matplotlib.sourceforge.net/matplotlib.axes.html#Axes“matplotlib . axes . axes]类是大多数绘图方法的所在地,因此在您完成创建一个图形后,这是一个很好的起点。
为了您的启迪,一系列 pylab 示例已被翻译成 OO API。它们在必须从命令行运行的演示脚本中可用。您可以使用任何交互式 matplotlib 后端来显示这些图。
简单的应用
这里有一个用 wx 编写的应用的简单例子,它嵌入了[“wx 面板中的 Matplotlib 图形”]。没有工具栏,鼠标点击或任何这些,只是一个在面板中绘制的图。已经做了一些工作来确保图形在调整大小时只重画一次。对于点多的地块,重绘可能需要一些时间,所以最好只在图形发布时重绘。读一读代码。
Matplotlib: pyside
Matplotlib: pyside
这是一个非常基本的例子,展示了如何使用 PySide 在 Qt 应用中显示 matplotlib 图。如果出现问题,尝试将 rcParam 条目“后端. qt4”更改为“PySide”(例如 matplotlibrc 文件中的 by)。
#!/usr/bin/env python
import sys
import matplotlib
matplotlib.use('Qt4Agg')
import pylab
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PySide import QtCore, QtGui
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
# generate the plot
fig = Figure(figsize=(600,600), dpi=72, facecolor=(1,1,1), edgecolor=(0,0,0))
ax = fig.add_subplot(111)
ax.plot([0,1])
# generate the canvas to display the plot
canvas = FigureCanvas(fig)
win = QtGui.QMainWindow()
# add the plot canvas to a window
win.setCentralWidget(canvas)
win.show()
sys.exit(app.exec_())
Matplotlib:卷动出图
Matplotlib:卷动出图
使用 wx 滚动条控制嵌入式绘图
当在 wxPython 应用中嵌入的 matplotlib 画布中绘制一个很长的序列时,有时能够显示序列的一部分而不求助于可滚动窗口以使两个轴保持可见是很有用的。下面是如何做到这一点:
from numpy import arange, sin, pi, float, size
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
from matplotlib.figure import Figure
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self,parent, id, 'scrollable plot',
style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER,
size=(800, 400))
self.panel = wx.Panel(self, -1)
self.fig = Figure((5, 4), 75)
self.canvas = FigureCanvasWxAgg(self.panel, -1, self.fig)
self.scroll_range = 400
self.canvas.SetScrollbar(wx.HORIZONTAL, 0, 5,
self.scroll_range)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, -1, wx.EXPAND)
self.panel.SetSizer(sizer)
self.panel.Fit()
self.init_data()
self.init_plot()
self.canvas.Bind(wx.EVT_SCROLLWIN, self.OnScrollEvt)
def init_data(self):
# Generate some data to plot:
self.dt = 0.01
self.t = arange(0,5,self.dt)
self.x = sin(2*pi*self.t)
# Extents of data sequence:
self.i_min = 0
self.i_max = len(self.t)
# Size of plot window:
self.i_window = 100
# Indices of data interval to be plotted:
self.i_start = 0
self.i_end = self.i_start + self.i_window
def init_plot(self):
self.axes = self.fig.add_subplot(111)
self.plot_data = \
self.axes.plot(self.t[self.i_start:self.i_end],
self.x[self.i_start:self.i_end])[0]
def draw_plot(self):
# Update data in plot:
self.plot_data.set_xdata(self.t[self.i_start:self.i_end])
self.plot_data.set_ydata(self.x[self.i_start:self.i_end])
# Adjust plot limits:
self.axes.set_xlim((min(self.t[self.i_start:self.i_end]),
max(self.t[self.i_start:self.i_end])))
self.axes.set_ylim((min(self.x[self.i_start:self.i_end]),
max(self.x[self.i_start:self.i_end])))
# Redraw:
self.canvas.draw()
def OnScrollEvt(self, event):
# Update the indices of the plot:
self.i_start = self.i_min + event.GetPosition()
self.i_end = self.i_min + self.i_window + event.GetPosition()
self.draw_plot()
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(parent=None,id=-1)
self.frame.Show()
self.SetTopWindow(self.frame)
return True
if __name__ == '__main__':
app = MyApp()
app.MainLoop()
六、Matplotlib / 杂项
- 加载图像
- Matplotlib:调整图像尺寸
- Matplotlib:编译 solaris10 上的 matplotlib
- Matplotlib:删除已有数据系列
- Matplotlib: django
- Matplotlib:交互绘图
- 缩放时处理点击事件
- Matplotlib: matplotlib 和 zope
- Matplotlib:多个子剧情,一个轴标签
- Matplotlib:带 ipython 和设计师的 Qt
- Matplotlib:在 CGI 脚本中使用 Matplotlib
负载图像
负载图像
图像处理通常对存储为 PNG 文件的灰度图像起作用。我们如何将该文件导入/导出到{{{python}}}?
- 下面是使用{{{imread}}}函数对 Matplotlib 执行此操作的副本(您的图像称为{{{lena.png}}})。
from pylab import imread, imshow, gray, mean
a = imread('lena.png')
# generates a RGB image, so do
aa=mean(a,2) # to get a 2-D array
imshow(aa)
gray()
这允许为进一步导出进行一些处理,例如[:Cookbook/Matplotlib/converting _ a _ matrix _ to _ a _ raster _ image:将矩阵转换为光栅图像]。在最新版本的 pylab 中(检查您的{{{pylab.matplotlib. 版本 }}}优于{{{'0.98.0'}}}),如果图像是灰度的,您将直接获得一个 2D numpy 数组。
- 若要写入图像,请执行以下操作:导入图像模式= 'L' size= (256,256) imNew=Image.new(模式,大小)mat = numpy.random.uniform(大小=大小)data = numpy . ravel(mat)data = numpy . floor(数据* 256)
imNew.putdata(date) imNew.save("rand.png")
- 这类函数也存在于{{{scipy.misc}}}下,例如参见{{{scipy.misc.imsave}}}创建彩色图像:从 scipy . misc import imsave import numpy a = numpy . zeros((4,4,3)) a[0,0,] = [128,0,255]imsave(' gray background _ with _ a _ greish _ blue _ square _ on _ top . png ',a)
- 要定义范围,请使用:从 scipy.misc import 到 image import numpy a = numpy . random . rand(25,50)# 0 之间。和 1。toimage(a,cmin=0。,cmax=2。).保存(' low_contrast_snow.png ')(改编自http://telin.ugent.be/~slippens/drupal/scipy_unscaledimsave
- http://jehiah.cz/archive/creating-images-with-numpy 提出了另一种(更直接的)方法
Matplotlib:调整图像大小
Matplotlib:调整图像大小
这是一个小的演示文件,帮助教授如何调整 matplotlib 的图形大小
首先简单介绍一下
有三个参数定义图像大小(这不是 MPL 特定的):
* Size in length units (inches, cm, pt, etc): e.g. 5"x7"``* Size in pixels: e.g. 800x600 pixels``* Dots per inch (dpi) e.g. 100 dpi
Only two of these are independent, so if you define two of them, the third can be calculated from the others.
当在计算机屏幕上显示(或保存为 PNG)时,长度单位的大小无关紧要,只是显示像素。当打印或保存到 PS、EPS 或 PDF(都设计为支持打印)时,将使用大小或 dpi 来确定如何缩放图像。
现在我开始研究 MPL 是如何工作的
. 1) The size of a figure is defined in length units (inches), and can be set by``. 2) The layout of the figure is defined in 'figure units' so that as the figure size is changed, the layout (eg axes positions) will update.``. 3) Size of text, width of lines, etc is defined in terms of length units (points?).``. 4) When displaying to the screen, or creating an image (PNG) the pixel size of text and line widths, etc is determined by the dpi setting, which is set by
The trick here is that when printing, it's natural to think in terms of inches, but when creating an image (for a web page, for instance), it is natural to think in terms of pixel size. However, as of 0.84, pixel size can only be set directly in the GTK* back-ends, with the canvas.resize(w,h) method. (remember that you can only set two of the three size parameters, the third must be calculated from the other two).
另一个技巧
Figure.savefig()覆盖了图中的 dpi 设置,并使用默认值(在我的系统上至少是 100 dpi)。如果要覆盖它,可以在 savefig 调用中指定“dpi ”:
下面的代码有望使这一点变得更加清楚,至少对于为网页等生成 png 来说是如此。
#!python
"""
This is a small demo file that helps teach how to adjust figure sizes
for matplotlib
"""
import matplotlib
print "using MPL version:", matplotlib.__version__
matplotlib.use("WXAgg") # do this before pylab so you don'tget the default back end.
import pylab
import matplotlib.numerix as N
# Generate and plot some simple data:
x = N.arange(0, 2*N.pi, 0.1)
y = N.sin(x)
pylab.plot(x,y)
F = pylab.gcf()
# Now check everything with the defaults:
DPI = F.get_dpi()
print "DPI:", DPI
DefaultSize = F.get_size_inches()
print "Default size in Inches", DefaultSize
print "Which should result in a %i x %i Image"%(DPI*DefaultSize[0], DPI*DefaultSize[1])
# the default is 100dpi for savefig:
F.savefig("test1.jpg")
# this gives me a 797 x 566 pixel image, which is about 100 DPI
# Now make the image twice as big, while keeping the fonts and all the
# same size
F.set_figsize_inches( (DefaultSize[0]*2, DefaultSize[1]*2) )
Size = F.get_size_inches()
print "Size in Inches", Size
F.savefig("test2.jpg")
# this results in a 1595x1132 image
# Now make the image twice as big, making all the fonts and lines
# bigger too.
F.set_figsize_inches( DefaultSize )# resetthe size
Size = F.get_size_inches()
print "Size in Inches", Size
F.savefig("test3.jpg", dpi = (200)) # change the dpi
# this also results in a 1595x1132 image, but the fonts are larger.
在一个图形中放置多个图像
假设您有两个图像:100x100 和 100x50,您希望在图形中显示这两个图像,它们之间有 20 个像素的缓冲区(相对于图像像素),四周有 10 个像素的边框。
这个解决方案并不是特别面向对象,但至少它触及到了实际的细节。
#!python
def _calcsize(matrix1, matrix2, top=10, left=10, right=10, bottom=10, buffer=20, height=4, scale = 1.):
size1 = array(matrix1.shape) * scale
size2 = array(matrix2.shape) * scale
_width = float(size1[1] + size2[1] + left + right + buffer)
_height = float(max(size1[0], size2[0]) + top + bottom)
x1 = left / _width
y1 = bottom / _height
dx1 = size1[1] / _width
dy1 = size1[0] / _height
size1 = (x1, y1, dx1, dy1)
x2 = (size1[1] + left + buffer) / _width
y2 = bottom / _height
dx2 = size2[1] / _width
dy2 = size2[0] / _height
size2 = (x2, y2, dx2, dy2)
figure = pylab.figure(figsize=(_width * height / _height, height))
axis1 = apply(pylab.axes, size1)
pylab.imshow(X1, aspect='preserve')
axis2 = apply(pylab.axes, size2)
pylab.imshow(X2, aspect='preserve')
return axes1, axes2, figure
附件
Matplotlib:编译 solaris10 上的 matplotlib
Matplotlib:编译 solaris10 上的 matplotlib
如何在 solaris 10 上安装 sunstudio 和构建 matplotlib 可能会给出一些提示。
JDH 说:
嗨,埃里克——如果你成功了,那么我们将有令人信服的证据证明在 solaris 上编译 mpl 比放弃调味汁更容易。
嗯,事实证明,这比放弃调味汁(至少对我来说)容易,但只是微乎其微。最后,修复非常简单(如果您考虑重新编译 python 并手动调整自动生成的 pyconfig.h,无论如何都非常简单)。经过整整两天的评论,从周日开始以 76 种不同的方式重新编译所有东西和它的母亲,仔细研究了一大群 Solaris sys,屠宰了几只洁白的羔羊和一只纯黑色的绵羊,将骨头、肌腱和内脏包裹在双层脂肪中,并将祭品焚烧给德尔菲阿波罗,我找到了答案:
1 下载 Python 2.4.2
2 提取后运行。/configure,编辑生成的 pyconfig.h,如下所示:
i) if _XOPEN_SOURCE is defined to be 600 (i.e., if the line "#define _XOPEN_SOURCE 600" appears in the file), redefine it to 500
ii) if _XOPEN_SOURCE_EXTENDED is defined at all (i.e. if the line "#define _XOPEN_SOURCE_EXTENDED 1" appears in the file), comment out its definition
3 制作和制作安装
问题在于 Solaris 对 X/Open 标准的支持。长话短说,当且仅当您使用 ISO C99 编译器时,您可以使用开放组技术标准,第 6 期(xpg 6/UNIX 03/SUSv3)(_ XOPEN _ SOURCE = = 600)。如果使用 X/Open CAE 规范,第 5 期(xpg 5/UNIX 98/sus v2)(_ XOPEN _ SOURCE = = 500),就不用使用 ISO C99 编译器了。有关完整的详细信息,请参见 Solaris 头文件/usr/include/sys/feature _ tests . h。
这就是为什么 muhpubuh(又名 matplotlib——长话短说)在 Solaris 10 上编译的原因,如果你有足够的钱并且能够负担得起孙的 OpenStudio 10 编译器的话。gcc 还没有完全的 C99 支持。特别是,它缺乏对宽字符库的支持,使得构建失败。(例如,参见http://gcc.gnu.org/c99status.html。)
关于 Python.h 和 Solaris 的 wchar 问题的更多有用链接:
- http://lists . stemp . de/pipermail/rxvt-unicode/2005 Q2/000092 . html
- http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6395191
- http://mail . python . org/piper mail/patches/2005-06/017820 . html
- http://mail . python . org/piper mail/python-bugs-list/2005-11/030900 . html
Matplotlib:删除现有数据系列
Matplotlib:删除现有数据系列
每个轴实例都包含一个 lines 属性,它是图中数据系列的列表,按时间顺序添加。要删除特定的数据系列,只需删除行列表中适当的元素,并在必要时重新绘制。
互动会话的以下示例说明了:
>>> x = N.arange(10)
>>> fig = P.figure()
>>> ax = fig.add_subplot(111)
>>> ax.plot(x)
[<matplotlib.lines.Line2D instance at 0x427ce7ec>]
>>> ax.plot(x+10)
[<matplotlib.lines.Line2D instance at 0x427ce88c>]
>>> ax.plot(x+20)
[<matplotlib.lines.Line2D instance at 0x427ce9ac>]
>>> P.show()
>>> ax.lines
[<matplotlib.lines.Line2D instance at 0x427ce7ec>,
<matplotlib.lines.Line2D instance at 0x427ce88c>,
<matplotlib.lines.Line2D instance at 0x427ce9ac>]
>>> del ax.lines[1]
>>> P.show()
它将绘制三行,然后删除第二行。
Matplotlib:决哥
Matplotlib:决哥
使用 MatPlotLib 在 Django 网络服务中动态生成图表
您需要有一个工作的 Django 安装,加上 matplotlib。
示例 1 - PIL 缓冲区
# file charts.py
def simple(request):
import random
import django
import datetime
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.dates import DateFormatter
fig=Figure()
ax=fig.add_subplot(111)
x=[]
y=[]
now=datetime.datetime.now()
delta=datetime.timedelta(days=1)
for i in range(10):
x.append(now)
now+=delta
y.append(random.randint(0, 1000))
ax.plot_date(x, y, '-')
ax.xaxis.set_major_formatter(DateFormatter('%Y-%m-%d'))
fig.autofmt_xdate()
canvas=FigureCanvas(fig)
response=django.http.HttpResponse(content_type='image/png')
canvas.print_png(response)
return response
因为某些版本的 Internet Explorer 忽略了内容类型。网址应以“.”结尾。巴布亚新几内亚”。您可以在 urls.py 中创建一个条目,如下所示:
...
(r'^charts/simple.png$', 'myapp.views.charts.simple'),
...
Matplotlib:交互式绘图
Matplotlib:交互式绘图
交互式点识别
我发现仅仅通过点击就能识别一个图中的点是非常有用的。这个秘籍提供了一个相当简单的函子,可以连接到任何一个地块。我把它用于散点图和标准图。
因为通常情况下,数据集的多个视图分布在多个图形上,或者至少分布在多个轴上,所以我还提供了一个工具来将这些图链接在一起,这样单击一个图中的某个点将突出显示并识别所有其他链接图上的该数据点。
import math
import matplotlib.pyplot as plt
class AnnoteFinder(object):
"""callback for matplotlib to display an annotation when points are
clicked on. The point which is closest to the click and within
xtol and ytol is identified.
Register this function like this:
scatter(xdata, ydata)
af = AnnoteFinder(xdata, ydata, annotes)
connect('button_press_event', af)
"""
def __init__(self, xdata, ydata, annotes, ax=None, xtol=None, ytol=None):
self.data = list(zip(xdata, ydata, annotes))
if xtol is None:
xtol = ((max(xdata) - min(xdata))/float(len(xdata)))/2
if ytol is None:
ytol = ((max(ydata) - min(ydata))/float(len(ydata)))/2
self.xtol = xtol
self.ytol = ytol
if ax is None:
self.ax = plt.gca()
else:
self.ax = ax
self.drawnAnnotations = {}
self.links = []
def distance(self, x1, x2, y1, y2):
"""
return the distance between two points
"""
return(math.sqrt((x1 - x2)**2 + (y1 - y2)**2))
def __call__(self, event):
if event.inaxes:
clickX = event.xdata
clickY = event.ydata
if (self.ax is None) or (self.ax is event.inaxes):
annotes = []
# print(event.xdata, event.ydata)
for x, y, a in self.data:
# print(x, y, a)
if ((clickX-self.xtol < x < clickX+self.xtol) and
(clickY-self.ytol < y < clickY+self.ytol)):
annotes.append(
(self.distance(x, clickX, y, clickY), x, y, a))
if annotes:
annotes.sort()
distance, x, y, annote = annotes[0]
self.drawAnnote(event.inaxes, x, y, annote)
for l in self.links:
l.drawSpecificAnnote(annote)
def drawAnnote(self, ax, x, y, annote):
"""
Draw the annotation on the plot
"""
if (x, y) in self.drawnAnnotations:
markers = self.drawnAnnotations[(x, y)]
for m in markers:
m.set_visible(not m.get_visible())
self.ax.figure.canvas.draw_idle()
else:
t = ax.text(x, y, " - %s" % (annote),)
m = ax.scatter([x], [y], marker='d', c='r', zorder=100)
self.drawnAnnotations[(x, y)] = (t, m)
self.ax.figure.canvas.draw_idle()
def drawSpecificAnnote(self, annote):
annotesToDraw = [(x, y, a) for x, y, a in self.data if a == annote]
for x, y, a in annotesToDraw:
self.drawAnnote(self.ax, x, y, a)
要使用这个函子,您可以简单地做如下事情:
x = range(10)
y = range(10)
annotes = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
fig, ax = plt.subplots()
ax.scatter(x,y)
af = AnnoteFinder(x,y, annotes, ax=ax)
fig.canvas.mpl_connect('button_press_event', af)
plt.show()
这是相当有用的,但有时您会有一个数据集的多个视图,单击并识别一个图中的点并在另一个图中找到它是很有用的。下面的代码演示了这种联系,应该在多个轴或图形之间工作。
def linkAnnotationFinders(afs):
for i in range(len(afs)):
allButSelfAfs = afs[:i]+afs[i+1:]
afs[i].links.extend(allButSelfAfs)
subplot(121)
scatter(x,y)
af1 = AnnoteFinder(x,y, annotes)
connect('button_press_event', af1)
subplot(122)
scatter(x,y)
af2 = AnnoteFinder(x,y, annotes)
connect('button_press_event', af2)
linkAnnotationFinders([af1, af2])
我觉得这相当有用。通过子类化和重新定义 drive 这个简单的框架可以用来驱动更复杂的用户界面。
目前,当数据点的数量变大时,这种实现有点慢。我特别感兴趣的是人们可能提出的让这个过程更快更好的建议。
Matplotlib: matplotlib 和 zope
Matplotlib: matplotlib 和 zope
0\. Prerequisites: You need to have the following installed to successfully run this example: Zope, Matplotlib (on top of Zope's Python), Python Image Library (PIL). And one more thing - probably every body does this right, but just in case - zope instance home directory has to be writable, for following to work.
1\. Create a file (e.g. mpl.py) in INSTANCEHOME\Extensions:
import matplotlib
matplotlib.use('Agg')
from pylab import *
from os import *
from StringIO import StringIO
from PIL import Image as PILImage
from matplotlib.backends.backend_agg import FigureCanvasAgg
def chart(self):
clf()
img_dpi=72
width=400
height=300
fig=figure(dpi=img_dpi, figsize=(width/img_dpi, height/img_dpi))
x=arange(0, 2*pi+0.1, 0.1)
sine=plot(x, sin(x))
legend(sine, "y=sin x", "upper right")
xlabel('x')
ylabel('y=sin x')
grid(True)
canvas = FigureCanvasAgg(fig)
canvas.draw()
size = (int(canvas.figure.get_figwidth())*img_dpi, int(canvas.figure.get_figheight())*img_dpi)
buf=canvas.tostring_rgb()
im=PILImage.fromstring('RGB', size, buf, 'raw', 'RGB', 0, 1)
imgdata=StringIO()
im.save(imgdata, 'PNG')
self.REQUEST.RESPONSE.setHeader('Pragma', 'no-cache')
self.REQUEST.RESPONSE.setHeader('Content-Type', 'image/png')
return imgdata.getvalue()
- 然后在 ZMI 创建一个外部方法(例如 Id -> mplchart,模块名-> mpl,函数名-> chart)。
- 单击测试选项卡,您应该会看到正弦图。
Matplotlib:具有一个轴标签的多个子图
Matplotlib:具有一个轴标签的多个子图
使用单个轴标签来注释多个子图轴
当使用具有相同轴单位的多个子图时,单独标记每个轴是多余的,并且使得图形过于复杂。您可以使用位于绘图框中心的单个轴标签来标记多个子绘图轴。下面是如何做到的:
#!python
# note that this a code fragment...you will have to define your own data to plot
# Set up a whole-figure axes, with invisible axis, ticks, and ticklabels,
# which we use to get the xlabel and ylabel in the right place
bigAxes = pylab.axes(frameon=False) # hide frame
pylab.xticks([]) # don't want to see any ticks on this axis
pylab.yticks([])
# I'm using TeX for typesetting the labels--not necessary
pylab.ylabel(r'\textbf{Surface Concentration $(nmol/m^2)$}', size='medium')
pylab.xlabel(r'\textbf{Time (hours)}', size='medium')
# Create subplots and shift them up and to the right to keep tick labels
# from overlapping the axis labels defined above
topSubplot = pylab.subplot(2,1,1)
position = topSubplot.get_position()
position[0] = 0.15
position[1] = position[1] + 0.01
topSubplot.set_position(position)
pylab.errorbar(times150, average150)
bottomSubplot = pylab.subplot(2,1,2)
position = bottomSubplot.get_position()
position[0] = 0.15
position[1] = position[1] + 0.03
bottomSubplot.set_position(position)
pylab.errorbar(times300, average300)
或者,您可以使用下面的代码片段在您的支线剧情中共享 ylabels。另见附图图输出。)#
#!python
import pylab
figprops = dict(figsize=(8., 8\. / 1.618), dpi=128) # Figure properties
adjustprops = dict(left=0.1, bottom=0.1, right=0.97, top=0.93, wspace=0.2 hspace=0.2) # Subplot properties
fig = pylab.figure(**figprops) # New figure
fig.subplots_adjust(**adjustprops) # Tunes the subplot layout
ax = fig.add_subplot(3, 1, 1)
bx = fig.add_subplot(3, 1, 2, sharex=ax, sharey=ax)
cx = fig.add_subplot(3, 1, 3, sharex=ax, sharey=ax)
ax.plot([0,1,2], [2,3,4], 'k-')
bx.plot([0,1,2], [2,3,4], 'k-')
cx.plot([0,1,2], [2,3,4], 'k-')
pylab.setp(ax.get_xticklabels(), visible=False)
pylab.setp(bx.get_xticklabels(), visible=False)
bx.set_ylabel('This is a long label shared among more axes', fontsize=14)
cx.set_xlabel('And a shared x label', fontsize=14)
感谢 matplotlib 用户列表中的塞巴斯蒂安·克里格使用了这个技巧。
简单的功能,去掉多余的 XT signs,但保留底部的 XT signs(在 pylab 中工作)。把它和上面的片段结合起来,得到一个没有太多多余的好情节:
#!python
def rem_x():
'''Removes superfluous x ticks when multiple subplots share
their axis works only in pylab mode but can easily be rewritten
for api use'''
nr_ax=len(gcf().get_axes())
count=0
for z in gcf().get_axes():
if count == nr_ax-1: break
setp(z.get_xticklabels(),visible=False)
count+=1
上面第一个对我不适用。子图命令覆盖大轴。然而,我找到了一个简单得多的解决方案,为两个轴和一个依拉贝尔做一件体面的工作:
yyl=plt.ylabel(r '我希望垂直居中的最长标签')
yyl . set _ position((yyl . get _ position()[0],1)) #这表示使用底轴的顶部作为参考点。
yyl.set_verticalalignment('中心')
附件
Matplotlib:带有 ipython 和 designer 的 qt
Matplotlib:带有 ipython 和 designer 的 qt
在 Qt 应用中嵌入 Matplotlib 的示例代码在 embedding_in_qt.py 中给出。这个方法扩展了与其他强大工具集成的基本公式。特别是,我们将巨魔科技(Qt 的创建者)制作的图形用户界面创建工具的使用和通过 IPython 与正在运行的 Qt 应用交互的能力结合在一起。
亚历克斯·费多索夫发布了使用设计器的基本教程(以及相关的系统要求)。请在继续之前查看它。
然而,出于我们的目的,我们将在 Designer 中创建一个简单得多的设计。
打开设计器,创建一个新的“主窗口”:
当向导出现时,删除它建议生成的所有菜单和工具栏:
现在将自定义小部件添加到您的项目中,如下所示:
为这个新的小部件命名,并将大小设置为 400,300:
现在应该会出现在工具箱中。单击它,然后在已创建的表单中单击。如果您知道“垂直布局(Ctrl+L)”和“调整大小(Ctrl+J)”是什么,也可以在表单上执行这些操作(不选择小部件)。此时,您的工作空间可能如下所示:
现在我们需要输入“导入”设置,如这里指定的:
当然,您还需要包含的文件: mplwidget.py
。
Designer 中所有这些操作的产物是一个. ui 文件。因此,保存我们一直在处理的表单,并将其称为“mplwidget_tutorial.ui”。
造成这种混乱的一个原因是,Designer 会自动递增小部件实例的名称(Form1、Form2、matplotlibWidget1、matplotlibWidget2 等)。)因此,如果这些与我使用的不匹配,您可能需要对您的过程进行一些逻辑调整。
现在,我们使用工具 pyuic(包含在 PyQt 中)来转换这个。ui 文件导入到 Python 类中。这很容易通过以下方式实现:
pyuic mplwidget_tutorial.ui > mplwidget_tutorial.py
继续查看 mplwidget_tutorial.py 的内容,并将其与我得到的进行比较。你也可以看看)#看我的 mplwidget_tutorial.ui
现在,将调用写在自己的 main_mpl_tutorial.py
文件中很好,该文件非常短:#
from mplwidget_tutorial import *
f = Form1()
f.show()
然后,我们想要启动 ipython 并实例化该窗口。为了在这项工作中取得成功,您需要一堆包(每个包都有最低版本要求),比如 python2.3-ipython、python2.3-qt3 等等。不过,最重要的是 ipython >= 0.6.13(我认为)。从那个版本开始,有一个超级棒的特性添加了一个调用开关{{{-qthread}}},它在一个单独的线程中启动一个 QApplication,这样 ipython 提示符仍然可以与之交互。
因此,如果上面的命令对您不起作用,请检查版本。根据您的配置,它也可以作为“python2.3-ipython”调用。
好了,现在最酷的部分来了:互动。
而且,很容易回到设计器,添加一个按钮,重新运行 pyuic,你就有了另一个版本。
附件
designer_edit_custom_widgets.png
designer_form_settings_comment.png
designer_full_workspace.png
designer_new_widget.png
designer_newopen.png
designer_wizard.png
ipython_interacted.png
ipython_invoked.png
main_mpl_tutorial.py
mplwidget.py
mplwidget_tutorial.py
mplwidget_tutorial.ui
Matplotlib:在 CGI 脚本中使用 matplotlib
Matplotlib:在 CGI 脚本中使用 matplotlib
试图天真地在 python CGI 脚本中使用 matplotlib 很可能会导致以下错误:
...
352, in _get_configdir
raise RuntimeError("'%s' is not a writable dir; you must set
environment variable HOME to be a writable dir "%h)
RuntimeError: '<WebServer DocumentRoot>' is not a writable dir; you must set
environment variable HOME to be a writable dir
Matplotlib 需要环境变量 HOME 指向一个可写目录。实现这一点的一种方法是在运行时从 CGI 脚本中设置这个环境变量(另一种方法是修改文件,但这种方法不可移植)。以下模板可用于使用 matplotlib 创建 png 图像的 cgi:
#!/usr/bin/python
import os,sys
import cgi
import cgitb; cgitb.enable()
# set HOME environment variable to a directory the httpd server can write to
os.environ[ 'HOME' ] = '/tmp/'
import matplotlib
# chose a non-GUI backend
matplotlib.use( 'Agg' )
import pylab
#Deals with inputing data into python from the html form
form = cgi.FieldStorage()
# construct your plot
pylab.plot([1,2,3])
print "Content-Type: image/png\n"
# save the plot as a png and output directly to webserver
pylab.savefig( sys.stdout, format='png' )
然后可以通过网址访问该图像,如:http://localhost/showpng . py
如文件所述,某些后端不允许将输出发送到 sys.stdout。可以用以下内容替换最后一行来解决这个问题:
pylab.savefig( "tempfile.jpg", format='png' )
import shutil
shutil.copyfileobj(open("tempfile.jpg",'rb'), sys.stdout)
(当然,有必要创建和删除适当的临时文件,以便在生产中使用。)
七、Matplotlib / 伪彩色打印
- matplot lib:color map transformation
- Matplotlib:将矩阵转换为光栅图像
- Matplotlib:网格化不规则间隔的数据
- Matplotlib:动态加载颜色图
- Matplotlib:用特殊值绘制图像
- Matplotlib:展示彩色地图
Matplotlib: colormap 转换
Matplotlib: colormap 转换
对颜色向量进行操作
有没有想过反转颜色图,或者去饱和颜色图?下面是一个将函数应用于颜色映射表的例程:
def cmap_map(function,cmap):
""" Applies function (which should operate on vectors of shape 3:
[r, g, b], on colormap cmap. This routine will break any discontinuous points in a colormap.
"""
cdict = cmap._segmentdata
step_dict = {}
# Firt get the list of points where the segments start or end
for key in ('red','green','blue'): step_dict[key] = map(lambda x: x[0], cdict[key])
step_list = sum(step_dict.values(), [])
step_list = array(list(set(step_list)))
# Then compute the LUT, and apply the function to the LUT
reduced_cmap = lambda step : array(cmap(step)[0:3])
old_LUT = array(map( reduced_cmap, step_list))
new_LUT = array(map( function, old_LUT))
# Now try to make a minimal segment definition of the new LUT
cdict = {}
for i,key in enumerate(('red','green','blue')):
this_cdict = {}
for j,step in enumerate(step_list):
if step in step_dict[key]:
this_cdict[step] = new_LUT[j,i]
elif new_LUT[j,i]!=old_LUT[j,i]:
this_cdict[step] = new_LUT[j,i]
colorvector= map(lambda x: x + (x[1], ), this_cdict.items())
colorvector.sort()
cdict[key] = colorvector
return matplotlib.colors.LinearSegmentedColormap('colormap',cdict,1024)
让我们试一试:我想要一个喷射彩色地图,但是要轻一些,这样我就可以在上面画出一些东西:
light_jet = cmap_map(lambda x: x/2+0.5, cm.jet)
x,y=mgrid[1:2,1:10:0.1]
imshow(y, cmap=light_jet)
[]文件/附件/matplotlib _ colormap transformation/light _ jet 4 . png
作为比较,这是原始 jet 的样子:[](文件/附件/Matplotlib _ colormaptations/jet . png
指数运行
好的,但是如果你想改变颜色图的索引,而不是它的颜色。
def cmap_xmap(function,cmap):
""" Applies function, on the indices of colormap cmap. Beware, function
should map the [0, 1] segment to itself, or you are in for surprises.
See also cmap_xmap.
"""
cdict = cmap._segmentdata
function_to_map = lambda x : (function(x[0]), x[1], x[2])
for key in ('red','green','blue'): cdict[key] = map(function_to_map, cdict[key])
cdict[key].sort()
assert (cdict[key][0]<0 or cdict[key][-1]>1), "Resulting indices extend out of the [0, 1] segment."
return matplotlib.colors.LinearSegmentedColormap('colormap',cdict,1024)
离散色图
以下是如何离散化连续的颜色映射。
def cmap_discretize(cmap, N):
"""Return a discrete colormap from the continuous colormap cmap.
cmap: colormap instance, eg. cm.jet.
N: number of colors.
Example
x = resize(arange(100), (5,100))
djet = cmap_discretize(cm.jet, 5)
imshow(x, cmap=djet)
"""
if type(cmap) == str:
cmap = get_cmap(cmap)
colors_i = concatenate((linspace(0, 1., N), (0.,0.,0.,0.)))
colors_rgba = cmap(colors_i)
indices = linspace(0, 1., N+1)
cdict = {}
for ki,key in enumerate(('red','green','blue')):
cdict[key] = [ (indices[i], colors_rgba[i-1,ki], colors_rgba[i,ki]) for i in xrange(N+1) ]
# Return colormap object.
return matplotlib.colors.LinearSegmentedColormap(cmap.name + "_%d"%N, cdict, 1024)
举个例子,这就是你通过做{ { { cmap _ discrete ze(cm . jet,6)}}得到的结果。
附件
dicrete_jet1.png
discrete_jet.png
jet.png
light_jet.png
light_jet2.png
light_jet3.png
light_jet4.png
Matplotlib:将矩阵转换为光栅图像
Matplotlib:将矩阵转换为光栅图像
Scipy 提供了一个命令(imsave)来制作栅格(png,jpg...)来自 2D 数组的图像,每个像素对应于该数组的一个值。然而图像是黑白的。
这里有一个用 Matplotlib 实现这一点的例子,并使用一个颜色映射给图像赋予颜色。
from matplotlib.pyplot import *
from scipy import mgrid
def imsave(filename, X, **kwargs):
""" Homebrewed imsave to have nice colors... """
figsize=(array(X.shape)/100.0)[::-1]
rcParams.update({'figure.figsize':figsize})
fig = figure(figsize=figsize)
axes([0,0,1,1]) # Make the plot occupy the whole canvas
axis('off')
fig.set_size_inches(figsize)
imshow(X,origin='lower', **kwargs)
savefig(filename, facecolor='black', edgecolor='black', dpi=100)
close(fig)
X,Y=mgrid[-5:5:0.1,-5:5:0.1]
Z=sin(X**2+Y**2+1e-4)/(X**2+Y**2+1e-4) # Create the data to be plotted
imsave('imsave.png', Z, cmap=cm.hot )
imshow(imread('imsave.png'))
<matplotlib.image.AxesImage at 0x7f1733edf610>
附件
Matplotlib:网格化不规则间距的数据
Matplotlib:网格化不规则间距的数据
matplotlib 邮件列表中一个常见的问题是“如何绘制不规则间距数据的等高线图?”。答案是,首先你把它插入一个规则的网格。从 0.98.3 版本开始,matplotlib 提供了一个 griddata 函数,其行为类似于 matlab 版本。它对规则网格中不规则间隔的数据执行“自然邻近插值”,然后可以用等高线、imshow 或 pcolor 绘制。
例 1
这需要 Scipy 0.9:
import numpy as np
from scipy.interpolate import griddata
import matplotlib.pyplot as plt
import numpy.ma as ma
from numpy.random import uniform, seed
# make up some randomly distributed data
seed(1234)
npts = 200
x = uniform(-2,2,npts)
y = uniform(-2,2,npts)
z = x*np.exp(-x**2-y**2)
# define grid.
xi = np.linspace(-2.1,2.1,100)
yi = np.linspace(-2.1,2.1,100)
# grid the data.
zi = griddata((x, y), z, (xi[None,:], yi[:,None]), method='cubic')
# contour the gridded data, plotting dots at the randomly spaced data points.
CS = plt.contour(xi,yi,zi,15,linewidths=0.5,colors='k')
CS = plt.contourf(xi,yi,zi,15,cmap=plt.cm.jet)
plt.colorbar() # draw colorbar
# plot data points.
plt.scatter(x,y,marker='o',c='b',s=5)
plt.xlim(-2,2)
plt.ylim(-2,2)
plt.title('griddata test (%d points)' % npts)
plt.show()
例 2
import numpy as np
from matplotlib.mlab import griddata
import matplotlib.pyplot as plt
import numpy.ma as ma
from numpy.random import uniform
# make up some randomly distributed data
npts = 200
x = uniform(-2,2,npts)
y = uniform(-2,2,npts)
z = x*np.exp(-x**2-y**2)
# define grid.
xi = np.linspace(-2.1,2.1,100)
yi = np.linspace(-2.1,2.1,100)
# grid the data.
zi = griddata(x,y,z,xi,yi)
# contour the gridded data, plotting dots at the randomly spaced data points.
CS = plt.contour(xi,yi,zi,15,linewidths=0.5,colors='k')
CS = plt.contourf(xi,yi,zi,15,cmap=plt.cm.jet)
plt.colorbar() # draw colorbar
# plot data points.
plt.scatter(x,y,marker='o',c='b',s=5)
plt.xlim(-2,2)
plt.ylim(-2,2)
plt.title('griddata test (%d points)' % npts)
plt.show()
默认情况下,griddata 使用 scikits delaunay 包(包含在 matplotlib 中)进行自然邻域插值。不幸的是,delaunay 包是众所周知的失败,为一些近乎病态的情况。如果遇到其中一种情况,可以安装 matplotlib natgrid 工具包。一旦安装完毕,griddata 函数将使用它代替 delaunay 进行插值。natgrid 算法稍微健壮一点,但由于许可问题,不能包含在 matplotlib 中。
scipy 沙盒中的径向基函数模块也可用于插值/平滑 n 维的分散数据。详情见[“秘籍/RadialBasisFunctions”]。
例 3
下面的代码展示了一个不太健壮但可能更直观的方法。该函数采用三个 1D 数组,即两个独立的数据数组和一个相关的数据数组,并将它们归入一个 2D 网格。除此之外,代码还返回另外两个网格,一个网格中的每个面元值代表该面元中的点数,另一个网格中的每个面元包含该面元中包含的原始相关数组的索引。如果需要,这些可以进一步用于仓之间的插值。
本质上是奥卡姆剃刀方法对 matplotlib.mlab griddata 函数,因为两者产生相似的结果。
# griddata.py - 2010-07-11 ccampo
import numpy as np
def griddata(x, y, z, binsize=0.01, retbin=True, retloc=True):
"""
Place unevenly spaced 2D data on a grid by 2D binning (nearest
neighbor interpolation).
Parameters
----------
x : ndarray (1D)
The idependent data x-axis of the grid.
y : ndarray (1D)
The idependent data y-axis of the grid.
z : ndarray (1D)
The dependent data in the form z = f(x,y).
binsize : scalar, optional
The full width and height of each bin on the grid. If each
bin is a cube, then this is the x and y dimension. This is
the step in both directions, x and y. Defaults to 0.01.
retbin : boolean, optional
Function returns `bins` variable (see below for description)
if set to True. Defaults to True.
retloc : boolean, optional
Function returns `wherebins` variable (see below for description)
if set to True. Defaults to True.
Returns
-------
grid : ndarray (2D)
The evenly gridded data. The value of each cell is the median
value of the contents of the bin.
bins : ndarray (2D)
A grid the same shape as `grid`, except the value of each cell
is the number of points in that bin. Returns only if
`retbin` is set to True.
wherebin : list (2D)
A 2D list the same shape as `grid` and `bins` where each cell
contains the indicies of `z` which contain the values stored
in the particular bin.
Revisions
---------
2010-07-11 ccampo Initial version
"""
# get extrema values.
xmin, xmax = x.min(), x.max()
ymin, ymax = y.min(), y.max()
# make coordinate arrays.
xi = np.arange(xmin, xmax+binsize, binsize)
yi = np.arange(ymin, ymax+binsize, binsize)
xi, yi = np.meshgrid(xi,yi)
# make the grid.
grid = np.zeros(xi.shape, dtype=x.dtype)
nrow, ncol = grid.shape
if retbin: bins = np.copy(grid)
# create list in same shape as grid to store indices
if retloc:
wherebin = np.copy(grid)
wherebin = wherebin.tolist()
# fill in the grid.
for row in range(nrow):
for col in range(ncol):
xc = xi[row, col] # x coordinate.
yc = yi[row, col] # y coordinate.
# find the position that xc and yc correspond to.
posx = np.abs(x - xc)
posy = np.abs(y - yc)
ibin = np.logical_and(posx < binsize/2., posy < binsize/2.)
ind = np.where(ibin == True)[0]
# fill the bin.
bin = z[ibin]
if retloc: wherebin[row][col] = ind
if retbin: bins[row, col] = bin.size
if bin.size != 0:
binval = np.median(bin)
grid[row, col] = binval
else:
grid[row, col] = np.nan # fill empty bins with nans.
# return the grid
if retbin:
if retloc:
return grid, bins, wherebin
else:
return grid, bins
else:
if retloc:
return grid, wherebin
else:
return grid
下面的示例演示了此方法的用法。
import numpy as np
import matplotlib.pyplot as plt
import griddata
npr = np.random
npts = 3000\. # the total number of data points.
x = npr.normal(size=npts) # create some normally distributed dependent data in x.
y = npr.normal(size=npts) # ... do the same for y.
zorig = x**2 + y**2 # z is a function of the form z = f(x, y).
noise = npr.normal(scale=1.0, size=npts) # add a good amount of noise
z = zorig + noise # z = f(x, y) = x**2 + y**2
# plot some profiles / cross-sections for some visualization. our
# function is a symmetric, upward opening paraboloid z = x**2 + y**2.
# We expect it to be symmetric about and and y, attain a minimum on
# the origin and display minor Gaussian noise.
plt.ion() # pyplot interactive mode on
# x vs z cross-section. notice the noise.
plt.plot(x, z, '.')
plt.title('X vs Z=F(X,Y=constant)')
plt.xlabel('X')
plt.ylabel('Z')
# y vs z cross-section. notice the noise.
plt.plot(y, z, '.')
plt.title('Y vs Z=F(Y,X=constant)')
plt.xlabel('Y')
plt.ylabel('Z')
# now show the dependent data (x vs y). we could represent the z data
# as a third axis by either a 3d plot or contour plot, but we need to
# grid it first....
plt.plot(x, y, '.')
plt.title('X vs Y')
plt.xlabel('X')
plt.ylabel('Y')
# enter the gridding. imagine drawing a symmetrical grid over the
# plot above. the binsize is the width and height of one of the grid
# cells, or bins in units of x and y.
binsize = 0.3
grid, bins, binloc = griddata.griddata(x, y, z, binsize=binsize) # see this routine's docstring
# minimum values for colorbar. filter our nans which are in the grid
zmin = grid[np.where(np.isnan(grid) == False)].min()
zmax = grid[np.where(np.isnan(grid) == False)].max()
# colorbar stuff
palette = plt.matplotlib.colors.LinearSegmentedColormap('jet3',plt.cm.datad['jet'],2048)
palette.set_under(alpha=0.0)
# plot the results. first plot is x, y vs z, where z is a filled level plot.
extent = (x.min(), x.max(), y.min(), y.max()) # extent of the plot
plt.subplot(1, 2, 1)
plt.imshow(grid, extent=extent, cmap=palette, origin='lower', vmin=zmin, vmax=zmax, aspect='auto', interpolation='bilinear')
plt.xlabel('X values')
plt.ylabel('Y values')
plt.title('Z = F(X, Y)')
plt.colorbar()
# now show the number of points in each bin. since the independent data are
# Gaussian distributed, we expect a 2D Gaussian.
plt.subplot(1, 2, 2)
plt.imshow(bins, extent=extent, cmap=palette, origin='lower', vmin=0, vmax=bins.max(), aspect='auto', interpolation='bilinear')
plt.xlabel('X values')
plt.ylabel('Y values')
plt.title('X, Y vs The No. of Pts Per Bin')
plt.colorbar()
入库数据:
叠加在入库数据顶部的原始数据:
附件
bin.png
bin_small
bin_small.png
binned_data.png
griddataexample1.png
ppb.png
ppb_raw.png
raw.png
raw_small
raw_small.png
unbinned_data.png
Matplotlib:动态加载颜色图
Matplotlib:动态加载颜色图
在 matplotlib 邮件列表中的一个线程中,詹姆斯·博伊尔发布了一种从文件中加载彩色地图的方法。这里稍微修改了一下。
gmtColormap.py
def gmtColormap(fileName,GMTPath = None):
import colorsys
import Numeric
N = Numeric
if type(GMTPath) == type(None):
filePath = "/usr/local/cmaps/"+ fileName+".cpt"
else:
filePath = GMTPath+"/"+ fileName +".cpt"
try:
f = open(filePath)
except:
print "file ",filePath, "not found"
return None
lines = f.readlines()
f.close()
x = []
r = []
g = []
b = []
colorModel = "RGB"
for l in lines:
ls = l.split()
if l[0] == "#":
if ls[-1] == "HSV":
colorModel = "HSV"
continue
else:
continue
if ls[0] == "B" or ls[0] == "F" or ls[0] == "N":
pass
else:
x.append(float(ls[0]))
r.append(float(ls[1]))
g.append(float(ls[2]))
b.append(float(ls[3]))
xtemp = float(ls[4])
rtemp = float(ls[5])
gtemp = float(ls[6])
btemp = float(ls[7])
x.append(xtemp)
r.append(rtemp)
g.append(gtemp)
b.append(btemp)
nTable = len(r)
x = N.array( x , N.Float)
r = N.array( r , N.Float)
g = N.array( g , N.Float)
b = N.array( b , N.Float)
if colorModel == "HSV":
for i in range(r.shape[0]):
rr,gg,bb = colorsys.hsv_to_rgb(r[i]/360.,g[i],b[i])
r[i] = rr ; g[i] = gg ; b[i] = bb
if colorModel == "HSV":
for i in range(r.shape[0]):
rr,gg,bb = colorsys.hsv_to_rgb(r[i]/360.,g[i],b[i])
r[i] = rr ; g[i] = gg ; b[i] = bb
if colorModel == "RGB":
r = r/255.
g = g/255.
b = b/255.
xNorm = (x - x[0])/(x[-1] - x[0])
red = []
blue = []
green = []
for i in range(len(x)):
red.append([xNorm[i],r[i],r[i]])
green.append([xNorm[i],g[i],g[i]])
blue.append([xNorm[i],b[i],b[i]])
colorDict = {"red":red, "green":green, "blue":blue}
return (colorDict)
Matplotlib:用特殊值绘制图像
Matplotlib:用特殊值绘制图像
图像绘图需要数据、颜色图和标准化。一个常见的愿望是以指定的颜色显示缺失的数据或其他值。下面的代码显示了如何做到这一点的示例。
该代码创建了一个新的颜色映射子类和一个范数子类。
初始化需要一个值字典,颜色对。数据已经被认为是标准化的(除了被保留的哨兵)。标记值处的 RGB 值被指定的颜色替换。
除了一个子所有权之外,该类以标准方式规范化数据。接受“忽略”参数。忽略的值需要从规范化中排除,以便它们不会扭曲结果。
我使用了一个不是特别好的算法,明确地对数据进行排序,并使用第一个非标记值来定义最小值和最大值。这可能会有所改善,但对我来说是简单而充分的。数据然后被标准化,包括哨兵。最后,哨兵被替换。
from matplotlib.colors import Colormap, normalize
import matplotlib.numerix as nx
from types import IntType, FloatType, ListType
class SentinelMap(Colormap):
def __init__(self, cmap, sentinels={}):
# boilerplate stuff
self.N = cmap.N
self.name = 'SentinelMap'
self.cmap = cmap
self.sentinels = sentinels
for rgb in sentinels.values():
if len(rgb)!=3:
raise ValueError('sentinel color must be RGB')
def __call__(self, scaledImageData, alpha=1):
# assumes the data is already normalized (ignoring sentinels)
# clip to be on the safe side
rgbaValues = self.cmap(nx.clip(scaledImageData, 0.,1.))
#replace sentinel data with sentinel colors
for sentinel,rgb in self.sentinels.items():
r,g,b = rgb
rgbaValues[:,:,0] = nx.where(scaledImageData==sentinel, r, rgbaValues[:,:,0])
rgbaValues[:,:,1] = nx.where(scaledImageData==sentinel, g, rgbaValues[:,:,1])
rgbaValues[:,:,2] = nx.where(scaledImageData==sentinel, b, rgbaValues[:,:,2])
rgbaValues[:,:,3] = nx.where(scaledImageData==sentinel, alpha, rgbaValues[:,:,3])
return rgbaValues
class SentinelNorm(normalize):
"""
Leave the sentinel unchanged
"""
def __init__(self, ignore=[], vmin=None, vmax=None):
self.vmin=vmin
self.vmax=vmax
if type(ignore) in [IntType, FloatType]:
self.ignore = [ignore]
else:
self.ignore = list(ignore)
def __call__(self, value):
vmin = self.vmin
vmax = self.vmax
if type(value) in [IntType, FloatType]:
vtype = 'scalar'
val = array([value])
else:
vtype = 'array'
val = nx.asarray(value)
# if both vmin is None and vmax is None, we'll automatically
# norm the data to vmin/vmax of the actual data, so the
# clipping step won't be needed.
if vmin is None and vmax is None:
needs_clipping = False
else:
needs_clipping = True
if vmin is None or vmax is None:
rval = nx.ravel(val)
#do this if sentinels (values to ignore in data)
if self.ignore:
sortValues=nx.sort(rval)
if vmin is None:
# find the lowest non-sentinel value
for thisVal in sortValues:
if thisVal not in self.ignore:
vmin=thisVal #vmin is the lowest non-sentinel value
break
else:
vmin=0.
if vmax is None:
for thisVal in sortValues[::-1]:
if thisVal not in self.ignore:
vmax=thisVal #vmax is the greatest non-sentinel value
break
else:
vmax=0.
else:
if vmin is None: vmin = min(rval)
if vmax is None: vmax = max(rval)
if vmin > vmax:
raise ValueError("minvalue must be less than or equal to maxvalue")
elif vmin==vmax:
return 0.*value
else:
if needs_clipping:
val = nx.clip(val,vmin, vmax)
result = (1.0/(vmax-vmin))*(val-vmin)
# replace sentinels with original (non-normalized) values
for thisIgnore in self.ignore:
result = nx.where(val==thisIgnore,thisIgnore,result)
if vtype == 'scalar':
result = result[0]
return result
if __name__=="__main__":
import pylab
import matplotlib.colors
n=100
# create a random array
X = nx.mlab.rand(n,n)
cmBase = pylab.cm.jet
# plot it array as an image
pylab.figure(1)
pylab.imshow(X, cmap=cmBase, interpolation='nearest')
# define the sentinels
sentinel1 = -10
sentinel2 = 10
# replace some data with sentinels
X[int(.1*n):int(.2*n), int(.5*n):int(.7*n)] = sentinel1
X[int(.6*n):int(.8*n), int(.2*n):int(.3*n)] = sentinel2
# define the colormap and norm
rgb1 = (0.,0.,0.)
rgb2 = (1.,0.,0.)
cmap = SentinelMap(cmBase, sentinels={sentinel1:rgb1,sentinel2:rgb2,})
norm = SentinelNorm(ignore=[sentinel1,sentinel2])
# plot with the modified colormap and norm
pylab.figure(2)
pylab.imshow(X, cmap = cmap, norm=norm, interpolation='nearest')
pylab.show()
如果前面的代码是从提示符运行的,则会生成两个图像。第一个是随机数据的原始图像。第二个图像是通过将一些块设置为哨兵值,然后以特定颜色绘制哨兵而修改的数据。示例结果如下所示。
附件
Matplotlib:显示颜色贴图
Matplotlib:显示颜色贴图
显示 Matplotlib 颜色映射
#!python
from pylab import *
from numpy import outer
rc('text', usetex=False)
a=outer(arange(0,1,0.01),ones(10))
figure(figsize=(10,5))
subplots_adjust(top=0.8,bottom=0.05,left=0.01,right=0.99)
maps=[m for m in cm.datad if not m.endswith("_r")]
maps.sort()
l=len(maps)+1
for i, m in enumerate(maps):
subplot(1,l,i+1)
axis("off")
imshow(a,aspect='auto',cmap=get_cmap(m),origin="lower")
title(m,rotation=90,fontsize=10)
savefig("colormaps.jpg",dpi=100,facecolor='gray')
但是,如果我认为那些彩色地图很丑呢?好吧,就用 matplotlib.colors 自己做吧!LinearSegmentedColormap。
首先,创建一个脚本,将范围(0,1)映射到 RGB 光谱中的值。在本词典中,您将为每种颜色“红色”、“绿色”和“蓝色”设置一系列元组。每个颜色系列中的第一个元素需要从 0 到 1 排序,中间有任意的间距。现在,考虑下面“红色”系列中的(0.5,1.0,0.7)。这个元组表示在(0,1)范围内的 0.5 处,从低于 1.0 处插值,从 0.7 处插值。通常,每个元组中的后两个值是相同的,但是使用不同的值有助于在颜色映射中放置分隔符。这比听起来更容易理解,如这个简单的脚本所示:
#!python
from pylab import *
cdict = {'red': ((0.0, 0.0, 0.0),
(0.5, 1.0, 0.7),
(1.0, 1.0, 1.0)),
'green': ((0.0, 0.0, 0.0),
(0.5, 1.0, 0.0),
(1.0, 1.0, 1.0)),
'blue': ((0.0, 0.0, 0.0),
(0.5, 1.0, 0.0),
(1.0, 0.5, 1.0))}
my_cmap = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict,256)
pcolor(rand(10,10),cmap=my_cmap)
colorbar()
如你所见,彩色地图中途有一个缺口。请负责任地使用这种新力量。
这是上面代码的一个稍加修改的版本,允许显示预定义的彩色地图以及自行创建的注册彩色地图。请注意,cm 模块中的 cmap_d 字典没有记录。在 distributed _ cmap 中选择索引颜色有些冒险...
"""Python colormaps demo
includes:
examples for registering own color maps
utility for showing all or selected named colormaps including self-defined ones"""
import matplotlib
import matplotlib.colors as col
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import numpy as np
def register_own_cmaps():
"""define two example colormaps as segmented lists and register them"""
# a good guide for choosing colors is provided at
# http://geography.uoregon.edu/datagraphics/color_scales.htm
#
# example 1:
# create own colormap from purple, blue, green, orange to red
# cdict contains a tuple structure for 'red', 'green', and 'blue'.
# Each color has a list of (x,y0,y1) tuples, where
# x defines the "index" in the colormap (range 0..1), y0 is the
# color value (0..1) left of x, and y1 the color value right of x.
# The LinearSegmentedColormap method will linearly interpolate between
# (x[i],y1) and (x[i+1],y0)
# The gamma value denotes a "gamma curve" value which adjusts the brightness
# at the bottom and top of the colormap. According to matlab documentation
# this means:
# colormap values are modified as c^gamma, where gamma is (1-beta) for
# beta>0 and 1/(1+beta) for beta<=0
cdict = {'red': ((0.0, 0.0, 0.0),
(0.3, 0.5, 0.5),
(0.6, 0.7, 0.7),
(0.9, 0.8, 0.8),
(1.0, 0.8, 0.8)),
'green': ((0.0, 0.0, 0.0),
(0.3, 0.8, 0.8),
(0.6, 0.7, 0.7),
(0.9, 0.0, 0.0),
(1.0, 0.7, 0.7)),
'blue': ((0.0, 1.0, 1.0),
(0.3, 1.0, 1.0),
(0.6, 0.0, 0.0),
(0.9, 0.0, 0.0),
(1.0, 1.0, 1.0))}
cmap1 = col.LinearSegmentedColormap('my_colormap',cdict,N=256,gamma=0.75)
cm.register_cmap(name='own1', cmap=cmap1)
# example 2: use the "fromList() method
startcolor = '#586323' # a dark olive
midcolor = '#fcffc9' # a bright yellow
endcolor = '#bd2309' # medium dark red
cmap2 = col.LinearSegmentedColormap.from_list('own2',[startcolor,midcolor,endcolor])
# extra arguments are N=256, gamma=1.0
cm.register_cmap(cmap=cmap2)
# we can skip name here as it was already defined
def discrete_cmap(N=8):
"""create a colormap with N (N<15) discrete colors and register it"""
# define individual colors as hex values
cpool = [ '#bd2309', '#bbb12d', '#1480fa', '#14fa2f', '#000000',
'#faf214', '#2edfea', '#ea2ec4', '#ea2e40', '#cdcdcd',
'#577a4d', '#2e46c0', '#f59422', '#219774', '#8086d9' ]
cmap3 = col.ListedColormap(cpool[0:N], 'indexed')
cm.register_cmap(cmap=cmap3)
def show_cmaps(names=None):
"""display all colormaps included in the names list. If names is None, all
defined colormaps will be shown."""
# base code from http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps
matplotlib.rc('text', usetex=False)
a=np.outer(np.arange(0,1,0.01),np.ones(10)) # pseudo image data
f=plt.figure(figsize=(10,5))
f.subplots_adjust(top=0.8,bottom=0.05,left=0.01,right=0.99)
# get list of all colormap names
# this only obtains names of built-in colormaps:
maps=[m for m in cm.datad if not m.endswith("_r")]
# use undocumented cmap_d dictionary instead
maps = [m for m in cm.cmap_d if not m.endswith("_r")]
maps.sort()
# determine number of subplots to make
l=len(maps)+1
if names is not None: l=len(names) # assume all names are correct!
# loop over maps and plot the selected ones
i=0
for m in maps:
if names is None or m in names:
i+=1
ax = plt.subplot(1,l,i)
ax.axis("off")
plt.imshow(a,aspect='auto',cmap=cm.get_cmap(m),origin="lower")
plt.title(m,rotation=90,fontsize=10,verticalalignment='bottom')
plt.savefig("colormaps.jpg",dpi=100,facecolor='gray')
if __name__ == "__main__":
register_own_cmaps()
discrete_cmap(8)
show_cmaps(['indexed','Blues','OrRd','PiYG','PuOr',
'RdYlBu','RdYlGn','afmhot','binary','copper',
'gist_ncar','gist_rainbow','own1','own2'])
附件
八、Matplotlib / 简单绘图
- Matplotlib:动画
- Matplotlib:阿罗兹
- matplot lib:bar chart
- Matplotlib:自定义日志标签
- Matplotlib:图表上的提示
- Matplotlib: legend
- Matplotlib:地图
- Matplotlib:多色线
- Matplotlib:多行打印
- Matplotlib:用屏蔽数组绘制值
- Matplotlib:阴影区域
- Matplotlib:乙状线函数
- Matplotlib:粗轴
- Matplotlib:转换
- Matplotlib:未填充的直方图
Matplotlib:动画
Matplotlib:动画
注: 本秘籍条目中的一些 matplotlib 代码可能已被弃用或过时。例如下面提到的文件 anim.py 在 matplotlib 中已经不存在了。matplotlib 中的动画示例见http://matplotlib . SourceForge . net/examples/animation/index . html,主要动画 API 文档为http://matplotlib.sourceforge.net/api/animation_api.html。
matplotlib 支持动画情节,并提供了大量演示。在考虑是否将 matplotlib 用于动画时,一个重要的问题是您需要什么样的速度。matplotlib 不是西方最快的绘图库,对于某些动画应用来说可能太慢了。尽管如此,它对许多人来说足够快,如果不是大多数人的话,本教程旨在向您展示如何让它对您来说足够快。特别是动画所选绘图元素一节向您展示了如何从 matplotlib 动画中获得最高速度。
性能
matplotlib 支持 5 种不同的图形用户界面(GTK、WX、Qt、Tkinter、FLTK),对于其中一些图形用户界面,有各种各样的方法可以绘制到画布上。例如,对于 GTK,可以使用原生的 GDK 绘图、安提格雷或开罗。一个图形用户界面工具包结合了一些绘图方法,包括一个后端。例如,使用 antigrain 绘图工具包绘制到 GTK 画布上称为 GTKAgg 后端。这一点很重要,因为不同的后端具有不同的性能特征,差异可能相当大。
考虑性能时,典型的度量是每秒帧数。电视是每秒 30 帧,对于许多应用,如果你能获得每秒 10 帧或更多的帧,动画就足够流畅,可以“看起来很好”。显示器通常以每秒 75-80 帧的速度刷新,因此这是性能的上限。更快的速度可能会浪费 CPU 周期。
以下是动画脚本 anim.py 的一些数字,它只是在各种后端更新一个正弦波,在 3GHz 奔腾 4 上的 linux 下运行。要在不同的后端下分析脚本,您可以使用下面描述的“图形用户界面中立”动画技术,然后使用标志运行它,如中所示:
> python anim.py -dWX
> python anim.py -dGTKAgg
以下是结果。请注意,应该谨慎地解释这些,因为有些图形用户界面可能在单独的线程中调用一个绘制操作,并在完成之前返回,或者在队列中有一个绘制操作时丢弃该操作。最重要的评估是定性的。
Backend Frames/second
GTK 43
GTKAgg 36
TkAgg 20
WX 11
WXAgg 27
pylab 中的图形用户界面中性动画
pylab 界面支持不依赖于特定 GUI 工具包的动画。不建议在制作中使用,但这通常是制作快速且简单的动画的好方法。导入 pylab 后,您需要使用[http://matplotlib.sf.net/matplotlib.pylab.html#-ion]命令打开交互。然后,您可以随时使用【http://matplotlib.sf.net/matplotlib.pylab.html#-抽奖抽奖】强制抽奖。在交互模式下,每个 pylab 命令后都会发出一个新的绘制命令,您也可以暂时关闭一组绘图命令的此行为,在这些命令中,您不希望使用[http://matplotlib.sf.net/matplotlib.pylab.html#-ioff ioff]命令进行更新。这在交互页面有更详细的描述。
这是 anim.py 脚本,用于生成上表中后端的分析数据
from pylab import *
import time
ion()
tstart = time.time() # for profiling
x = arange(0,2*pi,0.01) # x-array
line, = plot(x,sin(x))
for i in arange(1,200):
line.set_ydata(sin(x+i/10.0)) # update the data
draw() # redraw the canvas
print 'FPS:' , 200/(time.time()-tstart)
注意通过调用【http://matplotlib.sf.net/matplotlib.pylab.html#-绘制图】来创建一条线的技巧:
line, = plot(x,sin(x))
然后用 set_ydata 方法设置其数据并调用 draw:
line.set_ydata(sin(x+i/10.0)) # update the data
draw() # redraw the canvas
这比清除轴和/或为每个绘图命令创建新对象要快得多。
要激活 pcolor 绘图,请使用:
p = pcolor(XI,YI,C[0])
for i in range(1,len(C)):
p.set_array(C[i,0:-1,0:-1].ravel())
p.autoscale()
draw()
这假设 C 是一个 3D 数组,其中第一维是时间,XI,YI 和 C[i,:]具有相同的形状。如果 C[i,,]小一行一列,只需使用 C.ravel()。
使用图形用户界面定时器或空闲处理程序
如果你正在做生产代码或任何半严肃的事情,建议你使用特定于你的动画工具包的图形用户界面事件处理,因为这将使你比 matplotlib 通过图形用户界面中立的 pylab 界面对动画提供更多的控制。如何做到这一点取决于您的工具包,但是 matplotlib 示例目录中有几个后端的示例,例如, anim_tk.py 代表 Tkinter, dynamic_image_gtkagg.py 代表 gtkagg, dynamic_image_wxagg.py 代表 wxagg。
基本思想是创建你的身材和一个更新你身材的回调函数。然后将回调传递给图形用户界面空闲处理程序或计时器。GTK 的一个简单例子如下
def callback(*args):
line.set_ydata(get_next_plot())
canvas.draw() # or simply "draw" in pylab
gtk.idle_add(callback)
WX 或 WXAgg 中的一个简单示例如下所示
def callback(*args):
line.set_ydata(get_next_plot())
canvas.draw()
wx.WakeUpIdle() # ensure that the idle event keeps firing
wx.EVT_IDLE(wx.GetApp(), callback)
为选定的绘图元素设置动画
上面介绍的方法的一个限制是,每次调用绘制时,所有的图形元素都会被重新绘制,但是我们只更新了一个元素。通常我们想做的是画一个背景,并在它上面制作一两个元素的动画。截止 matplotlib-0.87,GTKAgg,!TkAgg、WXAgg 和 FLTKAgg 支持这里讨论的方法。
基本思想是设置您想要制作动画的艺术家的“动画”属性(从图到轴到线 2D 到文本的所有图形元素都是从基类艺术家派生的)。然后,当调用标准画布绘制操作时,除了动画艺术家之外的所有艺术家都将被绘制。然后,您可以使用方法将矩形区域(例如轴边界框)复制到像素缓冲区中。在动画中,您使用恢复背景,然后调用将动画艺术家绘制到干净的背景上,并将更新后的坐标轴矩形块复制到图形中。当我在为上面的 GTKAgg 生成 36 FPS 的相同环境中运行下面的示例时,我使用下面的技术测量了 327 FPS。请参见上面提到的关于性能数字的注意事项。简单地说,从数量和质量上来说,它要快得多。
import sys
import gtk, gobject
import pylab as p
import matplotlib.numerix as nx
import time
ax = p.subplot(111)
canvas = ax.figure.canvas
# for profiling
tstart = time.time()
# create the initial line
x = nx.arange(0,2*nx.pi,0.01)
line, = p.plot(x, nx.sin(x), animated=True)
# save the clean slate background -- everything but the animated line
# is drawn and saved in the pixel buffer background
background = canvas.copy_from_bbox(ax.bbox)
def update_line(*args):
# restore the clean slate background
canvas.restore_region(background)
# update the data
line.set_ydata(nx.sin(x+update_line.cnt/10.0))
# just draw the animated artist
ax.draw_artist(line)
# just redraw the axes rectangle
canvas.blit(ax.bbox)
if update_line.cnt==50:
# print the timing info and quit
print 'FPS:' , update_line.cnt/(time.time()-tstart)
sys.exit()
update_line.cnt += 1
return True
update_line.cnt = 0
gobject.idle_add(update_line)
p.show()
示例:光标移动
matplotlib 0.83.2 引入了一个游标类,它可以利用这些 blit 方法进行无滞后游标。该类在构造函数中接受一个参数。对于支持新 API 集的后端:
from matplotlib.widgets import Cursor
import pylab
fig = pylab.figure(figsize=(8,6))
ax = fig.add_axes([0.075, 0.25, 0.9, 0.725], axisbg='#FFFFCC')
x,y = 4*(pylab.rand(2,100)-.5)
ax.plot(x,y,'o')
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
# set useblit = True on gtkagg for enhanced performance
cursor = Cursor(ax, useblit=True, color='red', linewidth=2 )
pylab.show()
“blit”动画方法
如上所述,只有 GTKAgg 支持上面的方法来制作选定演员的动画。需要以下内容
图形画布方法
* `` - copy the region in ax.bbox into a pixel buffer and return it in an object type of your choosing. bbox is a matplotlib BBox instance from the ```pytransforms``module
<【http://matplotlib.sf.net/transforms.html】>__
。is not used by the matplotlib frontend, but it stores it and passes it back to the backend in the
方法。您可能不仅想存储像素缓冲区,还想存储背景对象中的矩形画布区域
* `` - restore the region copied above to the canvas.
* `` - transfer the pixel buffer in region bounded by bbox to the canvas.
对于*Agg 后端,没有必要实现前两个,因为 Agg 将完成所有工作(定义它们)。因此,您只需要能够从一个选定的矩形 blit agg 缓冲区。对于使用字符串方法将 agg 像素缓冲区传输到各自画布的后端来说,有一件事可能会使这变得更容易,那就是在 agg 中定义一个方法。如果您正在进行这项工作并需要帮助,请联系matplotlib-dev 列表。
一旦所有/大多数后端都实现了这些方法,matplotlib 前端就可以完成管理后台/恢复/复制操作的所有工作,userland 动画代码看起来就像
line, = plot(something, animated=True)
draw()
def callback(*args):
line.set_ydata(somedata)
ax.draw_animated()
```py
其余的将自动发生。由于某些后端‘‘‘‘目前没有实现所需的方法,所以我让用户可以自行管理它们,但不在轴绘图代码中假设它们。
# Matplotlib: arrows
# Matplotlib: arrows
一些使用箭头函数绘制箭头的示例代码。
from matplotlib.pyplot import *
from numpy import *
x = arange(10)
y = x
Plot junk and then a filled region
plot(x, y)
Now lets make an arrow object
arr = Arrow(2, 2, 1, 1, edgecolor='white')
Get the subplot that we are currently working on
ax = gca()
Now add the arrow
ax.add_patch(arr)
We should be able to make modifications to the arrow.
Lets make it green.
arr.set_facecolor('g')
![http://scipy-cookbook.readthedocs.org/_img/Matplotlib_Arrows_1_0.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/18a1d74c.jpg)
## 附件
* [`plot_arrow.png`](../_downloads/plot_arrow.jpg)
![http://scipy-cookbook.readthedocs.org/_img/plot_arrow.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/1b65a020.jpg)
# Matplotlib:栏图
# Matplotlib:栏图
使用条形图功能制作条形图:[http://matplotlib.sourceforge.net/matplotlib.pylab.html](http://matplotlib.sourceforge.net/matplotlib.pylab.html)#-条形图
下面是一个示例脚本,它使一个带有误差线和位于误差线中心的标签的条形成为字符。
!/usr/bin/env python
import numpy as na
from matplotlib.pyplot import *
labels = ["Baseline", "System"]
data = [3.75 , 4.75]
error = [0.3497 , 0.3108]
xlocations = na.array(range(len(data)))+0.5
width = 0.5
bar(xlocations, data, yerr=error, width=width)
yticks(range(0, 8))
xticks(xlocations+ width/2, labels)
xlim(0, xlocations[-1]+width*2)
title("Average Ratings on the Training Set")
gca().get_xaxis().tick_bottom()
gca().get_yaxis().tick_left()
show()
![http://scipy-cookbook.readthedocs.org/_img/Matplotlib_BarCharts_1_0.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/c3467f56.jpg)
## 附件
* [`barchart.png`](../_downloads/barchart.jpg)
![http://scipy-cookbook.readthedocs.org/_img/barchart.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/6f53c747.jpg)
# Matplotlib:自定义日志标签
# Matplotlib:自定义日志标签
如何用整数标签替换默认对数图指数标签的示例。同样的方法适用于任何类型的定制标签。这个例子是从 Python 列表邮件列表中提取的,原文可以在[这里](http://mail.python.org/pipermail/python-list/2006-February/369976.html)找到。
from matplotlib.pyplot import *
def log_10_product(x, pos):
"""The two args are the value and tick position.
Label ticks with the product of the exponentiation"""
return '%1i' % (x)
Axis scale must be set prior to declaring the Formatter
If it is not the Formatter will use the default log labels for ticks.
ax = subplot(111)
ax.set_xscale('log')
ax.set_yscale('log')
formatter = FuncFormatter(log_10_product)
ax.xaxis.set_major_formatter(formatter)
ax.yaxis.set_major_formatter(formatter)
Create some artificial data.
result1 = [3, 5, 70, 700, 900]
result2 = [1000, 2000, 3000, 4000, 5000]
predict1 = [4, 8, 120, 160, 200]
predict2 = [2000, 4000, 6000, 8000, 1000]
Plot
ax.scatter(result1, predict1, s=40, c='b', marker='s', faceted=False)
ax.scatter(result2, predict2, s=40, c='r', marker='s', faceted=False)
ax.set_xlim(1e-1, 1e4)
ax.set_ylim(1e-1, 1e4)
grid(True)
xlabel(r"Result", fontsize = 12)
ylabel(r"Prediction", fontsize = 12)
/usr/lib/python2.7/dist-packages/matplotlib/cbook.py:137: MatplotlibDeprecationWarning: The faceted option was deprecated in version 1.2. Use edgecolor instead.
warnings.warn(message, mplDeprecation, stacklevel=1)
<matplotlib.text.Text at 0x7f07874f2410>
![http://scipy-cookbook.readthedocs.org/_img/Matplotlib_CustomLogLabels_1_2.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/ab68ee71.jpg)
## 附件
* [`log_labels.png`](../_downloads/log_labels.jpg)
![http://scipy-cookbook.readthedocs.org/_img/log_labels.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/674b61fc.jpg)
# Matplotlib:图表上的提示
# Matplotlib:图表上的提示
## 带有 matplotlib 的 Hinton 图
Hinton 图是一种将矩阵/向量中的数值可视化的方法,在神经网络和机器学习文献中很流行。正方形所占的面积与值的大小成正比,颜色(在这种情况下是黑色或白色)表示它的符号(正/负)。
![image0](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/f9f40011.jpg) )#
import numpy as N
import pylab as P
def _blob(x,y,area,colour):
"""
Draws a square-shaped blob with the given area (< 1) at
the given coordinates.
"""
hs = N.sqrt(area) / 2
xcorners = N.array([x - hs, x + hs, x + hs, x - hs])
ycorners = N.array([y - hs, y - hs, y + hs, y + hs])
P.fill(xcorners, ycorners, colour, edgecolor=colour)
def hinton(W, maxWeight=None):
"""
Draws a Hinton diagram for visualizing a weight matrix.
Temporarily disables matplotlib interactive mode if it is on,
otherwise this takes forever.
"""
reenable = False
if P.isinteractive():
P.ioff()
P.clf()
height, width = W.shape
if not maxWeight:
maxWeight = 2**N.ceil(N.log(N.max(N.abs(W)))/N.log(2))
P.fill(N.array([0,width,width,0]),N.array([0,0,height,height]),'gray')
P.axis('off')
P.axis('equal')
for x in xrange(width):
for y in xrange(height):
_x = x+1
_y = y+1
w = W[y,x]
if w > 0:
_blob(_x - 0.5, height - _y + 0.5, min(1,w/maxWeight),'white')
elif w < 0:
_blob(_x - 0.5, height - _y + 0.5, min(1,-w/maxWeight),'black')
if reenable:
P.ion()
P.show()
## 附件
* [`hinton.png`](../_downloads/hinton.jpg)
![http://scipy-cookbook.readthedocs.org/_img/hinton.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/f9f40011.jpg)
# Matplotlib:图例
# Matplotlib:图例
## 重叠线条和标记的图例
如果你有很多点要画,你可能想每 *n* 点只设置一次标记,例如
plot(x, y, '-r')
plot(x[::20], y[::20], 'ro')
然后自动图例将此视为两个不同的图。一种方法是创建一个额外的线对象,该对象不绘制在任何地方,而仅用于图例:
from matplotlib.lines import Line2D
line = Line2D(range(10), range(10), linestyle='-', marker='o')
legend((line,), (label,))
另一种可能是修改图例中的线条对象:
line = plot(x, y, '-r')
markers = plot(x[::20], y[::20], 'ro')
lgd = legend([line], ['data'], numpoints=3)
lgd.get_lines()[0].set_marker('o')
draw()
## 剧情外的传奇
没有很好的简单方法可以在你的剧情外(右边)添加一个图例,但是如果你一开始就设置好了轴,它就可以工作了:
figure()
axes([0.1,0.1,0.71,0.8])
plot([0,1],[0,1],label="line 1")
hold(1)
plot([0,1],[1,0.5],label="line 2")
legend(loc=(1.03,0.2))
show()
## 从情节中移除图例
可以简单地将轴的属性设置为并重新绘制:
ax = gca()
ax.legend_ = None
draw()
如果您发现自己经常这样做,请使用以下函数
def remove_legend(ax=None):
"""Remove legend for ax or the current axes."""
from pylab import gca, draw
if ax is None:
ax = gca()
ax.legend_ = None
draw()
(来源: [Re:如何用 matplotlib OO 从图中删除图例?](http://osdir.com/ml/python.matplotlib.general/2005-07/msg00285.html))
## 更改图例文本的字体大小
请注意,要设置默认字体大小,可以更改 matplotlib rc 参数文件中的 *legend.size* 属性。
要仅更改一个绘图的字体大小,请使用 matplotlib.font *管理器。FontProperties 参数,_prop* ,用于图例创建。
x = range(10)
y = range(10)
handles = plot(x, y)
legend(handles, ["label1"], prop={"size":12})
# Matplotlib:贴图
# Matplotlib:贴图
使用底图工具包可以轻松地在地图投影上绘制数据。工具包是扩展 matplotlib 的特定于应用的函数的集合。
**底图工具包不在默认 matplotlib 安装**中-可以从 matplotlib sourceforge [下载页面](http://sourceforge.net/project/showfiles.php?group_id=80706&package_id=142792)下载。
假设你想用正投影或卫星投影制作一张世界地图,并在上面绘制一些数据。以下是如何制作地图(使用 matplotlib >= 0.98.0 和底图> = 0.99):
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
set up orthographic map projection with
perspective of satellite looking down at 50N, 100W.
use low resolution coastlines.
don't plot features that are smaller than 1000 square km.
map = Basemap(projection='ortho', lat_0 = 50, lon_0 = -100,
resolution = 'l', area_thresh = 1000.)
draw coastlines, country boundaries, fill continents.
map.drawcoastlines()
map.drawcountries()
map.fillcontinents(color = 'coral')
draw the edge of the map projection region (the projection limb)
map.drawmapboundary()
draw lat/lon grid lines every 30 degrees.
map.drawmeridians(np.arange(0, 360, 30))
map.drawparallels(np.arange(-90, 90, 30))
plt.show()
还有许多其他可用的地图投影,可能比你以前听说过的还要多。完整列表可在[底图文档字符串](http://matplotlib.sourceforge.net/mpl_toolkits.basemap.basemap.html)中找到。海岸线、政治边界和河流有四种分辨率:、、和。这是分辨率海岸线的样子。
[](文件/附件/Matplotlib_Maps/basemap0.png
现在,假设你想在这张地图上画出五个城市的位置。在上述脚本中的之前添加以下内容:
lat/lon coordinates of five cities.
lats = [40.02, 32.73, 38.55, 48.25, 17.29]
lons = [-105.16, -117.16, -77.00, -114.21, -88.10]
cities=['Boulder, CO','San Diego, CA',
'Washington, DC','Whitefish, MT','Belize City, Belize']
compute the native map projection coordinates for cities.
x,y = map(lons,lats)
plot filled circles at the locations of the cities.
map.plot(x,y,'bo')
plot the names of those five cities.
for name,xpt,ypt in zip(cities,x,y):
plt.text(xpt+50000,ypt+50000,name)
[](文件/附件/Matplotlib_Maps/basemap1b.png
使用经度和纬度数组调用底图类实例会使用[项目 4](http://proj.maptools.org) 库返回原生地图投影坐标中的这些位置。现在假设您有一些关于常规纬度/经度网格的数据,并且您想要在地图上绘制这些数据的等高线。尝试在前面添加以下几行
make up some data on a regular lat/lon grid.
nlats = 73; nlons = 145; delta = 2.np.pi/(nlons-1)
lats = (0.5np.pi-deltanp.indices((nlats,nlons))[0,:,:])
lons = (deltanp.indices((nlats,nlons))[1,:,:])
wave = 0.75(np.sin(2.lats)8np.cos(4.lons))
mean = 0.5np.cos(2.lats)((np.sin(2.lats))2 + 2.)
compute native map projection coordinates of lat/lon grid.
x, y = map(lons180./np.pi, lats180./np.pi)
contour data over the map.
CS = map.contour(x,y,wave+mean,15,linewidths=1.5)
[](文件/附件/Matplotlib_Maps/basemap2b.png
您可以使用方法将图像用作地图背景,而不是绘制大陆和海岸线。默认的背景图像是 NASA 的“蓝色大理石”图像,您可以使用
map.bluemarble()
代替
map.drawcoastlines()
map.drawcountries()
map.fillcontinents(color='coral')
下面是结果图的样子(用白色文字代替黑色,用白点代替蓝色)
![](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/a60d8ced.jpg)
您还可以在地图投影上绘制图像、pcolor 图和矢量。在底图源分布的示例目录中可以找到说明这一点和更多内容的示例。
## 附件
* [`basemap0.png`](../_downloads/basemap0.jpg)
* [`basemap1.png`](../_downloads/basemap1.jpg)
* [`basemap1b.png`](../_downloads/basemap1b.jpg)
* [`basemap2.png`](../_downloads/basemap2.jpg)
* [`basemap2b.png`](../_downloads/basemap2b.jpg)
* [`basemap3.png`](../_downloads/basemap3.jpg)
* [`basemap3b.png`](../_downloads/basemap3b.jpg)
* [`basemap3c.png`](../_downloads/basemap3c.jpg)
![http://scipy-cookbook.readthedocs.org/_img/basemap0.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/5bd08415.jpg) ![http://scipy-cookbook.readthedocs.org/_img/basemap1.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/66b0ada5.jpg) ![http://scipy-cookbook.readthedocs.org/_img/basemap1b.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/d6a50456.jpg) ![http://scipy-cookbook.readthedocs.org/_img/basemap2.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/2110d775.jpg) ![http://scipy-cookbook.readthedocs.org/_img/basemap2b.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/503176f8.jpg) ![http://scipy-cookbook.readthedocs.org/_img/basemap3.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/1c70fec5.jpg) ![http://scipy-cookbook.readthedocs.org/_img/basemap3b.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/9b6da55d.jpg) ![http://scipy-cookbook.readthedocs.org/_img/basemap3c.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/a60d8ced.jpg)
# Matplotlib:多色线
# Matplotlib:多色线
## 手动定义颜色
[`colored_line.py`](../_downloads/colored_line.py) 是一个简单的例子,说明了如何使一条线的每一段的颜色依赖于所绘制数据的某些属性。
脚本的最新版本可以在[这里](http://matplotlib.sourceforge.net/gallery.html)找到。
![](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/281cc25c.jpg)
以下是脚本:
!/usr/bin/env python
'''
Color parts of a line based on its properties, e.g., slope.
'''
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
x = np.linspace(0, 3 * np.pi, 500)
y = np.sin(x)
z = np.cos(0.5 * (x[:-1] + x[1:])) # first derivative
Create a colormap for red, green and blue and a norm to color
f' < -0.5 red, f' > 0.5 blue, and the rest green
cmap = ListedColormap(['r', 'g', 'b'])
norm = BoundaryNorm([-1, -0.5, 0.5, 1], cmap.N)
Create a set of line segments so that we can color them individually
This creates the points as a N x 1 x 2 array so that we can stack points
together easily to get the segments. The segments array for line collection
needs to be numlines x points per line x 2 (x and y)
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
Create the line collection object, setting the colormapping parameters.
Have to set the actual values used for colormapping separately.
lc = LineCollection(segments, cmap=cmap, norm=norm)
lc.set_array(z)
lc.set_linewidth(3)
plt.gca().add_collection(lc)
plt.xlim(x.min(), x.max())
plt.ylim(-1.1, 1.1)
plt.show()
请注意,线段数比点数少一个。
另一种策略是为给定颜色的每个连续区域生成一个片段。
## 使用平滑的内置彩色地图
如果要显示参数曲线,并且希望使用颜色表示参数。
![](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/2a992d4a.jpg)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
t = np.linspace(0, 10, 200)
x = np.cos(np.pi * t)
y = np.sin(t)
Create a set of line segments so that we can color them individually
This creates the points as a N x 1 x 2 array so that we can stack points
together easily to get the segments. The segments array for line collection
needs to be numlines x points per line x 2 (x and y)
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
Create the line collection object, setting the colormapping parameters.
Have to set the actual values used for colormapping separately.
lc = LineCollection(segments, cmap=plt.get_cmap('copper'),
norm=plt.Normalize(0, 10))
lc.set_array(t)
lc.set_linewidth(3)
plt.gca().add_collection(lc)
plt.xlim(-1, 1)
plt.ylim(-1, 1)
plt.show()
## 附件
* [`colored_line.png`](../_downloads/colored_line.jpg)
* [`colored_line.py`](../_downloads/colored_line.py)
* [`colored_line2.png`](../_downloads/colored_line2.jpg)
![http://scipy-cookbook.readthedocs.org/_img/colored_line.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/281cc25c.jpg) ![http://scipy-cookbook.readthedocs.org/_img/colored_line2.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/2a992d4a.jpg)
# Matplotlib:多行打印
# Matplotlib:多行打印
## 多线图
人们常常想把许多信号画在另一个信号上。有几种方法可以做到这一点。最简单的实现就是给每个信号增加一个恒定的偏移:
from pylab import plot, show, ylim, yticks
from matplotlib.numerix import sin, cos, exp, pi, arange
t = arange(0.0, 2.0, 0.01)
s1 = sin(2pit)
s2 = exp(-t)
s3 = sin(2pit)exp(-t)
s4 = sin(2pit)cos(4pit)
t = arange(0.0, 2.0, 0.01)
plot(t, s1, t, s2+1, t, s3+2, t, s4+3, color='k')
ylim(-1,4)
yticks(arange(4), ['S1', 'S2', 'S3', 'S4'])
show()
但是很难以合理的方式改变 y 刻度。例如,当您放大 y 时,顶部和底部的信号将离开屏幕。通常我们想要的是每个信号的 y 位置保持不变,并且信号的增益被改变。
## 使用多轴
如果你只有几个信号,你可以把每个信号做成单独的轴,把 y 标签做成水平的。这对于少量信号(比如 4-10 个)来说效果不错,除了额外的水平线和轴周围的刻度可能会令人讨厌。改变这些轴线的绘制方式是我们的任务之一,这样你就可以去掉它,但是还没有完成:
from pylab import figure, show, setp
from matplotlib.numerix import sin, cos, exp, pi, arange
t = arange(0.0, 2.0, 0.01)
s1 = sin(2pit)
s2 = exp(-t)
s3 = sin(2pit)exp(-t)
s4 = sin(2pit)cos(4pit)
fig = figure()
t = arange(0.0, 2.0, 0.01)
yprops = dict(rotation=0,
horizontalalignment='right',
verticalalignment='center',
x=-0.01)
axprops = dict(yticks=[])
ax1 =fig.add_axes([0.1, 0.7, 0.8, 0.2], **axprops)
ax1.plot(t, s1)
ax1.set_ylabel('S1', **yprops)
axprops['sharex'] = ax1
axprops['sharey'] = ax1
force x axes to remain in register, even with toolbar navigation
ax2 = fig.add_axes([0.1, 0.5, 0.8, 0.2], **axprops)
ax2.plot(t, s2)
ax2.set_ylabel('S2', **yprops)
ax3 = fig.add_axes([0.1, 0.3, 0.8, 0.2], **axprops)
ax3.plot(t, s4)
ax3.set_ylabel('S3', **yprops)
ax4 = fig.add_axes([0.1, 0.1, 0.8, 0.2], **axprops)
ax4.plot(t, s4)
ax4.set_ylabel('S4', **yprops)
turn off x ticklabels for all but the lower axes
for ax in ax1, ax2, ax3:
setp(ax.get_xticklabels(), visible=False)
show()
[]文件/附件/matplotlib _ multi line plot/multiple axes . png
## 操纵变换
对于大量的线,上面的方法是低效的,因为为每条线创建单独的轴会产生大量无用的开销。催生 matplotlib 的应用是[脑电查看器](http://matplotlib.sourceforge.net/screenshots/eeg_small.jpg),它必须高效处理数百行;这是 [pbrain 套装](http://pbrain.sf.net)的一部分。
下面是一个例子,说明该应用如何在增益变化的情况下进行多线绘图。请注意,这将破坏工具栏的 y 行为,因为我们已经更改了所有默认转换。在我的应用中,我有一个自定义工具栏来增加或减少 y 比例。在这个例子中,我将加号/减号键绑定到一个增加或减少 y 增益的函数。也许我会把它打包成一个名为 plot_signals 的函数或类似的东西,因为代码有点复杂,因为它大量使用了有点晦涩的 matplotlib 变换。在试图理解这个例子之前,我建议阅读一下[转换模块](http://matplotlib.sourceforge.net/matplotlib.transforms.html):
from pylab import figure, show, setp, connect, draw
from matplotlib.numerix import sin, cos, exp, pi, arange
from matplotlib.numerix.mlab import mean
from matplotlib.transforms import Bbox, Value, Point,
get_bbox_transform, unit_bbox
load the data
t = arange(0.0, 2.0, 0.01)
s1 = sin(2pit)
s2 = exp(-t)
s3 = sin(2pit)exp(-t)
s4 = sin(2pit)cos(4pit)
s5 = s1s2
s6 = s1-s4
s7 = s3s4-s1
signals = s1, s2, s3, s4, s5, s6, s7
for sig in signals:
sig = sig-mean(sig)
lineprops = dict(linewidth=1, color='black', linestyle='-')
fig = figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
The normal matplotlib transformation is the view lim bounding box
(ax.viewLim) to the axes bounding box (ax.bbox). Where are going to
define a new transform by defining a new input bounding box. See the
matplotlib.transforms module helkp for more information on
transforms
This bounding reuses the x data of the viewLim for the xscale -10 to
10 on the y scale. -10 to 10 means that a signal with a min/max
amplitude of 10 will span the entire vertical extent of the axes
scale = 10
boxin = Bbox(
Point(ax.viewLim.ll().x(), Value(-scale)),
Point(ax.viewLim.ur().x(), Value(scale)))
height is a lazy value
height = ax.bbox.ur().y() - ax.bbox.ll().y()
boxout = Bbox(
Point(ax.bbox.ll().x(), Value(-0.5) * height),
Point(ax.bbox.ur().x(), Value( 0.5) * height))
matplotlib transforms can accepts an offset, which is defined as a
point and another transform to map that point to display. This
transform maps x as identity and maps the 0-1 y interval to the
vertical extent of the yaxis. This will be used to offset the lines
and ticks vertically
transOffset = get_bbox_transform(
unit_bbox(),
Bbox( Point( Value(0), ax.bbox.ll().y()),
Point( Value(1), ax.bbox.ur().y())
))
now add the signals, set the transform, and set the offset of each
line
ticklocs = []
for i, s in enumerate(signals):
trans = get_bbox_transform(boxin, boxout)
offset = (i+1.)/(len(signals)+1.)
trans.set_offset( (0, offset), transOffset)
ax.plot(t, s, transform=trans, **lineprops)
ticklocs.append(offset)
ax.set_yticks(ticklocs)
ax.set_yticklabels(['S%d'%(i+1) for i in range(len(signals))])
place all the y tick attributes in axes coords
all = []
labels = []
ax.set_yticks(ticklocs)
for tick in ax.yaxis.get_major_ticks():
all.extend(( tick.label1, tick.label2, tick.tick1line,
tick.tick2line, tick.gridline))
labels.append(tick.label1)
setp(all, transform=ax.transAxes)
setp(labels, x=-0.01)
ax.set_xlabel('time (s)')
Because we have hacked the transforms, you need a special method to
set the voltage gain; this is a naive implementation of how you
might want to do this in real life (eg make the scale changes
exponential rather than linear) but it gives you the idea
def set_ygain(direction):
set_ygain.scale += direction
if set_ygain.scale <=0:
set_ygain.scale -= direction
return
for line in ax.lines:
trans = line.get_transform()
box1 = trans.get_bbox1()
box1.intervaly().set_bounds(-set_ygain.scale, set_ygain.scale)
draw()
set_ygain.scale = scale
def keypress(event):
if event.key in ('+', '='): set_ygain(-1)
elif event.key in ('-', '_'): set_ygain(1)
connect('key_press_event', keypress)
ax.set_title('Use + / - to change y gain')
show()
## 附件
* [`multiline.png`](../_downloads/multiline.jpg)
* [`multipleaxes.png`](../_downloads/multipleaxes.jpg)
![http://scipy-cookbook.readthedocs.org/_img/multiline.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/70fe26c9.jpg) ![http://scipy-cookbook.readthedocs.org/_img/multipleaxes.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/ad15f45e.jpg)
# Matplotlib:用屏蔽数组绘制值
# Matplotlib:用屏蔽数组绘制值
有时一个人可能会在数组中得到“无意义”的数据。是因为探测器工作不正常还是其他原因。或者必须处理完全不同范围的数据。在这两种情况下,绘制所有值都会破坏绘图。这个简短的示例脚本解决了这个问题,并展示了一个使用屏蔽数组的可能解决方案。也可以参考 matplotlib 示例中的“masked_demo.py”。
import numpy as np
import matplotlib.pyplot as plt
y_values = [0,0,100,97,98,0,99,101,0,102,99,105,101]
x_values = [0,1,2,3,4,5,6,7,8,9,10,11,12]
give a threshold
threshold = 1
prepare for masking arrays - 'conventional' arrays won't do it
y_values = np.ma.array(y_values)
mask values below a certain threshold
y_values_masked = np.ma.masked_where(y_values < threshold , y_values)
plot all data
plt.subplots_adjust(hspace=0.5)
plt.subplot(311)
plt.plot(x_values, y_values,'ko')
plt.title('All values')
plt.subplot(312)
plt.plot(x_values, y_values_masked,'ko')
plt.title('Plot without masked values')
ax = plt.subplot(313)
ax.plot(x_values, y_values_masked,'ko')
for otherwise the range of x_values gets truncated:
ax.set_xlim(x_values[0], x_values[-1])
plt.title('Plot without masked values -\nwith full range x-axis')
savefig('masked_test.png')
结果图可能说明了这个问题——注意所有三个子情节中不同的尺度:
![](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/5dd19d95.jpg)
## 附件
* [`masked_test.png`](../_downloads/masked_test.jpg)
![http://scipy-cookbook.readthedocs.org/_img/masked_test.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/5dd19d95.jpg)
# Matplotlib:阴影区域
# Matplotlib:阴影区域
使用填充功能使任何颜色的阴影区域变浅。这里有一个例子。
from pylab import *
x = arange(10)
y = x
Plot junk and then a filled region
plot(x, y)
Make a blue box that is somewhat see-through
and has a red border.
WARNING: alpha doesn't work in postscript output....
fill([3,4,4,3], [2,2,4,4], 'b', alpha=0.2, edgecolor='r')
[<matplotlib.patches.Polygon at 0x7f7a19aac890>]
![http://scipy-cookbook.readthedocs.org/_img/Matplotlib_ShadedRegions_1_1.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/cab4916.jpg)
## 附件
* [`shaded.png`](../_downloads/shaded.jpg)
![http://scipy-cookbook.readthedocs.org/_img/shaded.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/6414dbe8.jpg)
# matplotlib:sigma ly 函数
# matplotlib:sigma ly 函数
matplotlib 绘制函数的方法要求您计算要绘制的曲线的 x 和 y 顶点,然后将其传递给绘图。例如对于普通的 pdf,matplotlib.mlab 提供了这样一个功能:
from matplotlib.mlab import normpdf
import matplotlib.numerix as nx
import pylab as p
x = nx.arange(-4, 4, 0.01)
y = normpdf(x, 0, 1) # unit normal
p.plot(x,y, color='red', lw=2)
p.show()
当然,有些曲线没有封闭形式的表达式,不适合这种处理。matplotlib 的一些后端能够用样条(三次和四次)绘制任意路径,但是这个功能还没有向用户公开(从 0.83 开始)。如有需要,请发送至[邮件列表](http://sourceforge.net/mail/?group_id=80706)或提交 sourceforge [支持请求](http://sourceforge.net/tracker/?group_id=80706&atid=560721)。
Rich Shepard 对绘制“S 曲线”和“Z 曲线”很感兴趣,通过一点谷歌搜索发现,S 曲线是一个 sigmoid,Z 曲线只是 1.0-sigmoid。sigmoids 有许多简单的形式:如希尔德函数、玻尔兹曼函数和反正切函数。下面是玻尔兹曼函数的一个例子:
import matplotlib.numerix as nx
import pylab as p
def boltzman(x, xmid, tau):
"""
evaluate the boltzman function with midpoint xmid and time constant tau
over x
"""
return 1. / (1. + nx.exp(-(x-xmid)/tau))
x = nx.arange(-6, 6, .01)
S = boltzman(x, 0, 1)
Z = 1-boltzman(x, 0.5, 1)
p.plot(x, S, x, Z, color='red', lw=2)
p.show()
另请参见数学世界中的[乙状结肠。](http://mathworld.wolfram.com/SigmoidFunction.html)
人们经常想给这些曲线下的区域着色,例如它们的交点下的[,这可以通过 numerix 和 matplotlib【](http://www.appl-ecosys.com/newstuff.html)[http://matplotlib.sourceforge.net/matplotlib.pylab.html](http://matplotlib.sourceforge.net/matplotlib.pylab.html)#-fill fill】函数来实现:
import matplotlib.numerix as nx
import pylab as p
def boltzman(x, xmid, tau):
"""
evaluate the boltzman function with midpoint xmid and time constant tau
over x
"""
return 1. / (1. + nx.exp(-(x-xmid)/tau))
def fill_below_intersection(x, S, Z):
"""
fill the region below the intersection of S and Z
"""
#find the intersection point
ind = nx.nonzero( nx.absolute(S-Z)==min(nx.absolute(S-Z)))[0]
# compute a new curve which we will fill below
Y = nx.zeros(S.shape, typecode=nx.Float)
Y[:ind] = S[:ind] # Y is S up to the intersection
Y[ind:] = Z[ind:] # and Z beyond it
p.fill(x, Y, facecolor='blue', alpha=0.5)
x = nx.arange(-6, 6, .01)
S = boltzman(x, 0, 1)
Z = 1-boltzman(x, 0.5, 1)
p.plot(x, S, x, Z, color='red', lw=2)
fill_below_intersection(x, S, Z)
p.show()
正如这些例子所说明的,matplotlib 并没有为人们想要绘制的所有类型的曲线提供帮助函数,但是与 numerix 和 python 一起提供了基本的工具,使您能够自己构建它们。
![](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/a0c11508.jpg)
## 附件
* [`sigmoids.png`](../_downloads/sigmoids.jpg)
* [`sigmoids2.png`](../_downloads/sigmoids2.jpg)
![http://scipy-cookbook.readthedocs.org/_img/sigmoids.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/15ed6c47.jpg) ![http://scipy-cookbook.readthedocs.org/_img/sigmoids2.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/a0c11508.jpg)
# Matplotlib:粗轴
# Matplotlib:粗轴
如何加厚绘图周围的线条(坐标轴线条)以及如何在刻度和坐标轴标签上获得大粗体字体的示例。
from pylab import *
Thicken the axes lines and labels
Comment by J. R. Lu:
I couldn't figure out a way to do this on the
individual plot and have it work with all backends
and in interactive mode. So, used rc instead.
rc('axes', linewidth=2)
Make a dummy plot
plot([0, 1], [0, 1])
Change size and font of tick labels
Again, this doesn't work in interactive mode.
fontsize = 14
ax = gca()
for tick in ax.xaxis.get_major_ticks():
tick.label1.set_fontsize(fontsize)
tick.label1.set_fontweight('bold')
for tick in ax.yaxis.get_major_ticks():
tick.label1.set_fontsize(fontsize)
tick.label1.set_fontweight('bold')
xlabel('X Axis', fontsize=16, fontweight='bold')
ylabel('Y Axis', fontsize=16, fontweight='bold')
Save figure
savefig('thick_axes.png')
![http://scipy-cookbook.readthedocs.org/_img/Matplotlib_ThickAxes_1_0.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/76921bf0.jpg)
## 附件
* [`thick_axes.png`](../_downloads/thick_axes.jpg)
![http://scipy-cookbook.readthedocs.org/_img/thick_axes.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/2f34e427.jpg)
# Matplotlib:转换
# Matplotlib:转换
每当你把坐标传给 matplotlib,问题就来了,你指的是什么样的坐标。考虑以下示例
axes.text(x,y, "my label")
标签“我的标签”被添加到坐标 x,y 的轴上,或者更清楚地说明:文本被放置在数据点(x,y)的理论位置。因此,我们称之为“数据坐标”。然而,人们可以想到其他坐标。例如,您可能希望在图表的正中间放置一个标签。如果您通过上面的方法指定了这一点,那么您将需要确定 x 和 y 的最小值和最大值来确定中间值。但是,使用转换,您可以简单地使用
axes.text(0.5, 0.5, "middle of graph", transform=axes.transAxes)
您应该知道四种内置转换(假设 ax 是 Axes 实例,fig 是 fig 实例):
matplotlib.transforms.identity_transform() # display coords
ax.transData # data coords
ax.transAxes # 0,0 is bottom,left of axes and 1,1 is top,right
fig.transFigure # 0,0 is bottom,left of figure and 1,1 is top,right
这些变换可以用于任何类型的艺术家,而不仅仅是文本对象。
ax.text 的默认转换是 ax.transData,而 fig.text 的默认转换是 fig . trans configure。
当然,您可以定义更一般的变换,例如 matplotlib.transforms.Affine,但是上面列出的四种变换出现在很多应用中。
xy_tup()不再存在。请参见[处的 Matplotlib 官方文档 http://Matplotlib . SourceForge . net/users/transforms _ tutorial . html](http://matplotlib.sourceforge.net/users/transforms_tutorial.html)以获取进一步参考。
## 示例:类似注释的刻度标签
如果您发现 Matplotlib 的内置刻度标签对您来说不够,您可以使用转换来实现类似的东西。下面是一个示例,该示例在刻度标签下方绘制注释,并使用变换来保证注释的 x 坐标对应于绘图的 x 坐标,但 y 坐标位于固定位置,与绘图的比例无关:
import matplotlib as M
import Numeric as N
import pylab as P
blend = M.transforms.blend_xy_sep_transform
def doplot(fig, subplot, function):
ax = fig.add_subplot(subplot)
x = N.arange(0, 2*N.pi, 0.05)
ax.plot(x, function(x))
trans = blend(ax.transData, ax.transAxes)
for x,text in [(0.0, '|'), (N.pi/2, r'$\rm{zero\ to\ }\pi$'),
(N.pi, '|'), (N.pi*1.5, r'$\pi\rm{\ to\ }2\pi$'),
(2*N.pi, '|')]:
ax.text(x, -0.1, text, transform=trans,
horizontalalignment='center')
fig = P.figure()
doplot(fig, 121, N.sin)
doplot(fig, 122, lambda x: 10*N.sin(x))
P.show()
## 示例:向数据坐标添加像素偏移
有时,您希望指定标签显示相对于相应数据点的固定*像素*偏移,而不考虑缩放。这里有一种方法;试着在一个交互式后端运行这个程序,缩放和平移图片。
其工作方式是首先获取`transData`的一个浅拷贝,然后添加一个偏移量。所有的变换都可以有一个偏移量,这个偏移量可以用`set_offset`修改,复制是为了避免修改数据本身的变换。足够新的 matplotlib 版本(目前只有 svn 版本)有一个`offset_copy`功能,可以自动完成这项工作。
import matplotlib
import matplotlib.transforms
from pylab import figure, show
New enough versions have offset_copy by Eric Firing:
if 'offset_copy' in dir(matplotlib.transforms):
from matplotlib.transforms import offset_copy
def offset(ax, x, y):
return offset_copy(ax.transData, x=x, y=y, units='dots')
else: # Without offset_copy we have to do some black transform magic
from matplotlib.transforms import blend_xy_sep_transform, identity_transform
def offset(ax, x, y):
# This trick makes a shallow copy of ax.transData (but fails for polar plots):
trans = blend_xy_sep_transform(ax.transData, ax.transData)
# Now we set the offset in pixels
trans.set_offset((x,y), identity_transform())
return trans
fig=figure()
ax=fig.add_subplot(111)
plot some data
x = (3,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3)
y = (2,7,1,8,2,8,1,8,2,8,4,5,9,0,4,5)
ax.plot(x,y,'.')
add labels
trans=offset(ax, 10, 5)
for a,b in zip(x,y):
ax.text(a, b, '(%d,%d)'%(a,b), transform=trans)
show()
# Matplotlib:未填充的直方图
# Matplotlib:未填充的直方图
下面是一些用于绘制直方图的模板代码,它们看起来不像条形图,而是只有轮廓(像 IDL 创建的那样)。
首先定义一个承担大部分繁重工作的函数。
import numpy as np
def histOutline(dataIn, *args, **kwargs):
(histIn, binsIn) = np.histogram(dataIn, *args, **kwargs)
stepSize = binsIn[1] - binsIn[0]
bins = np.zeros(len(binsIn)*2 + 2, dtype=np.float)
data = np.zeros(len(binsIn)*2 + 2, dtype=np.float)
for bb in range(len(binsIn)):
bins[2*bb + 1] = binsIn[bb]
bins[2*bb + 2] = binsIn[bb] + stepSize
if bb < len(histIn):
data[2*bb + 1] = histIn[bb]
data[2*bb + 2] = histIn[bb]
bins[0] = bins[1]
bins[-1] = bins[-2]
data[0] = 0
data[-1] = 0
return (bins, data)
现在,我们可以制作图表:
Make some data to plot
data = randn(500)
figure(2, figsize=(10, 5))
clf()
##########
First make a normal histogram
##########
subplot(1, 2, 1)
(n, bins, patches) = hist(data)
Boundaries
xlo = -max(abs(bins))
xhi = max(abs(bins))
ylo = 0
yhi = max(n) * 1.1
axis([xlo, xhi, ylo, yhi])
##########
Now make a histogram in outline format
##########
(bins, n) = histOutline(data)
subplot(1, 2, 2)
plot(bins, n, 'k-')
axis([xlo, xhi, ylo, yhi])
在下面,您可以找到打包到 histOutline.py 中的这个功能
## 附件
* [`histNofill.py`](../_downloads/histNofill.py)
* [`histOutline.py`](../_downloads/histOutline.py)
* [`hist_outline.png`](../_downloads/hist_outline.jpg)
![http://scipy-cookbook.readthedocs.org/_img/hist_outline.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/9f2cd8ca.jpg)
# 九、Matplotlib / 排版
* [Matplotlib: latex 示例](Matplotlib_LaTeX_Examples.html)
* [Matplotlib:使用 tex](Matplotlib_UsingTex.html)
# Matplotlib: latex examples
# Matplotlib: latex examples
## 使用 LaTeX 制作用于发布的图形
本页描述了用 LaTeX 制作出版物质量图形的几种方法。
### 乳胶加工
本节描述了一种遵循[“秘籍/Matplotlib/using tex”]指南的技术。
以下是用于包含该图的 LaTeX 文件的概要(例如`REVTeX4`用于出版的是具有两列格式的 APS 物理期刊。)
```py
\documentclass[prl,10pt,twocolumn]{revtex4}
\usepackage{graphicx} % Used to import the graphics
\begin{document}
%...
\begin{figure}[t]
\begin{center}
\showthe\columnwidth % Use this to determine the width of the figure.
\includegraphics[width=\columnwidth]{fig1.eps}
\caption{\label{fig:sin_cos} Plot of the sine and cosine functions.}
\end{center}
\end{figure}
%...
\end{document}
确定图形尺寸
第一步是确定图形的大小:这样,当包含图形时,它不会被调整大小,字体等。将与您设置的完全一样,而不是缩放(可能会失真)。这可以在 LaTeX 中通过显式设置图形的宽度并使用\showthe
命令打印该宽度来完成。(在上例中,图形宽度设置为\columnwidth
。)
当文件被 LaTeX 处理时,看看输出。以上示例产生以下输出(注意:LaTeX 将在\showthe
命令后暂停,按 enter 键继续):
This is TeX, Version 3.14159 (Web2C 7.4.5)
LaTeX2e <2001/06/01>
...
> 246.0pt.
l.8 \showthe\columnwidth
% Use this to determine the width of the figure.
?
<fig1.eps> [1] (./tst.aux) )
...
因此,该数字将为 246.0 磅宽。有 1 英寸= 72.27pt (in [La]TeX),所以这意味着图形宽度应为 3.40390 英寸。高度取决于图形的内容,但是中庸之道可以用来做出令人愉悦的图形。一旦确定了这一点,就可以使用figure.figsize
属性来设置默认的图形尺寸。
#!python numbers=disable
fig_width_pt = 246.0 # Get this from LaTeX using \showthe\columnwidth
inches_per_pt = 1.0/72.27 # Convert pt to inches
golden_mean = (sqrt(5)-1.0)/2.0 # Aesthetic ratio
fig_width = fig_width_pt*inches_per_pt # width in inches
fig_height =fig_width*golden_mean # height in inches
fig_size = [fig_width,fig_height]
设置字体大小
由于图形不会缩小,我们可以明确设置字体大小。
#!python numbers=disable
'font.size' : 10,
'axes.labelsize' : 10,
'font.size' : 10,
'text.fontsize' : 10,
'legend.fontsize': 10,
'xtick.labelsize' : 8,
'ytick.labelsize' : 8,
微调
对于这些较小的绘图尺寸,默认边距不足以显示轴标签,因此我们需要指定较大的边距。我们通过对axes()
函数的显式调用来做到这一点。在这个例子中,我们只有一个轴。排版后的 LaTeX 文档在图的两边都有空白,所以我们不需要在图中包含这个。因此,我们在顶部和右侧只保留一点空白,这样标签就不会超出边界框,并在底部为 x 标签添加更多空间:
#!python numbers=disable
pylab.axes([0.125,0.2,0.95-0.125,0.95-0.2])
把它们放在一起
下面是生成图的 python 文件。
#!python
import pylab
from pylab import arange,pi,sin,cos,sqrt
fig_width_pt = 246.0 # Get this from LaTeX using \showthe\columnwidth
inches_per_pt = 1.0/72.27 # Convert pt to inch
golden_mean = (sqrt(5)-1.0)/2.0 # Aesthetic ratio
fig_width = fig_width_pt*inches_per_pt # width in inches
fig_height = fig_width*golden_mean # height in inches
fig_size = [fig_width,fig_height]
params = {'backend': 'ps',
'axes.labelsize': 10,
'text.fontsize': 10,
'legend.fontsize': 10,
'xtick.labelsize': 8,
'ytick.labelsize': 8,
'text.usetex': True,
'figure.figsize': fig_size}
pylab.rcParams.update(params)
# Generate data
x = pylab.arange(-2*pi,2*pi,0.01)
y1 = sin(x)
y2 = cos(x)
# Plot data
pylab.figure(1)
pylab.clf()
pylab.axes([0.125,0.2,0.95-0.125,0.95-0.2])
pylab.plot(x,y1,'g:',label='$\sin(x)$')
pylab.plot(x,y2,'-b',label='$\cos(x)$')
pylab.xlabel('$x$ (radians)')
pylab.ylabel('$y$')
pylab.legend()
pylab.savefig('fig1.eps')
产生灰度虚线
An obvious solution is to greyscale convert your figure, but for readibility, adding dashes is often better.. This maybe implemented for an example of a SigmoidalFunctions with
使用 psfrag 的 LaTeX
注:此部分已过时。【matplotlib 的最新版本打破了 psfrag 功能(例如参见这一讨论)。也就是说,可以使用 usetex 特性直接渲染 LaTeX 文本,效果非常好(如果你在选择字体时很小心的话)。在不久的将来,我将尝试在这里进一步讨论这个问题。–迈克尔·麦克尼尔·福布斯
为了确保您的图形使用与文档完全相同的字体,您可以让 LaTeX 使用 psfrag 包生成并替换图形中的文本。如果您对text.usetex
方法有问题(例如,找不到合适的字体),这是一个很好的选择。)
为此,只需对标签使用纯文本,然后使用 psfrag 包替换它们。下面是使用这种方法的修改文件:
\documentclass[prl,10pt,twocolumn]{revtex4}
\usepackage{graphicx} % Used to import the graphics
\usepackage{psfrag}
\begin{document}
%...
\begin{figure}[t]
\begin{center}
\psfrag{sin(x)}{$\sin(x)$}
\psfrag{cos(x)}{$\cos(x)$}
\psfrag{x (radians)}{$x$ (radians)}
\psfrag{y}{$y$}
{\footnotesize % Replace tick-lables with smaller font.
\psfrag{1.0}{1.0}
\psfrag{0.5}{0.5}
\psfrag{0.0}{0.0}
\psfrag{-0.5}{-0.5}
\psfrag{-1.0}{-1.0}
\psfrag{-8}{-8}
\psfrag{-6}{-6}
\psfrag{-4}{-4}
\psfrag{-2}{-2}
\psfrag{0}{0}
\psfrag{2}{2}
\psfrag{4}{4}
\psfrag{6}{6}
\psfrag{8}{8}
\showthe\columnwidth % Use this to determine the width of the figure.
\includegraphics[width=\columnwidth]{fig2.eps}
} % Note that the psfrag commands only work in the top-most environment.
\caption{\label{fig:sin_cos} Plot of the sine and cosine functions.}
\end{center}
\end{figure}
%...
\end{document}
#!python
import pylab
from pylab import arange,pi,sin,cos,sqrt
fig_width_pt = 246.0 # Get this from LaTeX using \showthe\columnwidth
inches_per_pt = 1.0/72.27 # Convert pt to inch
golden_mean = (sqrt(5)-1.0)/2.0 # Aesthetic ratio
fig_width = fig_width_pt*inches_per_pt # width in inches
fig_height = fig_width*golden_mean # height in inches
fig_size = [fig_width,fig_height]
params = {'backend': 'ps',
'axes.labelsize': 10,
'text.fontsize': 10,
'legend.fontsize': 10,
'xtick.labelsize': 8,
'ytick.labelsize': 8,
'text.usetex': False,
'figure.figsize': fig_size}
pylab.rcParams.update(params)
# Generate data
x = pylab.arange(-2*pi,2*pi,0.01)
y1 = sin(x)
y2 = cos(x)
# Plot data
# Plot data
pylab.figure(1)
pylab.clf()
pylab.axes([0.125,0.2,0.95-0.125,0.95-0.2])
pylab.plot(x,y1,'g:',label='sin(x)')
pylab.plot(x,y2,'-b',label='cos(x)')
pylab.xlabel('x (radians)')
pylab.ylabel('y')
pylab.legend()
pylab.savefig('fig2.eps')
零碎东西
绘制图例后,设置图例字体的另一种方法是使用,例如:
from matplotlib.font_manager import fontManager, FontProperties
font= FontProperties(size='x-small');
pylab.legend(loc=0, prop=font);
大卫·多诺万
PDF 文件大小
如果您有非常复杂的图像,如高分辨率等高线图或三维图,将这些图像保存为矢量图形格式(如 PDF 或 EPS)可能会导致无法接受的大文件(尽管具有惊人的缩放能力)。一种解决方案是选择性地将部分绘图(而不是文本标签)转换为光栅化图像。这可以通过 matplotlib 最新版本中所谓的“混合模式渲染”功能来实现。这里有一个例子,它可能会起作用:
#!python
from pylab import meshgrid, sin, cos, linspace, contourf, savefig, clf
x, y = meshgrid(*(linspace(-1,1,500),)*2)
z = sin(20*x**2)*cos(30*y)
c = contourf(x,y,z,50)
savefig('full_vector.pdf')
clf()
c = contourf(x,y,z,50,rasterized=True)
savefig('rasterized.pdf')
但是,{{{contourf}}}当前不支持{ { {栅格化}}}选项(该选项被忽略)。然而,其他一些情节元素确实如此。我正在寻找解决办法。–迈克尔·麦克尼尔·福布斯
附件
Matplotlib:使用 tex
Matplotlib:使用 tex
Matplotlib 可以使用 LaTeX 来处理图形中的文本布局。这个选项(仍然是实验性的)可以通过在 rc 设置中设置 text.usetex : true 来激活。matplotlib 的 LaTeX 支持的文本处理比标准文本处理慢,但是更灵活,并且产生出版物质量的图。结果是惊人的,尤其是当你注意在你的图形中使用与主文档中相同的字体时。
Matplotlib 的 LaTeX 支持仍在开发中,尽管至少有两个人依靠它来为他们的博士论文生成数据。从 matplotlib-0.87 开始已经做了很多改进,如果你有更早的版本,请更新 matplotlib。此选项需要运行中的 LaTeX 安装、 dvipng (可能包含在您的 TeX 安装中)和 ghostscript ( AFPL、GPL 或 ESP ghostscript 应该都可以,但建议使用 GPL ghostscript-8.60 或更高版本)。这些外部依赖项的可执行文件必须位于您的路径上。
有几个选项值得一提,可以使用 rc 设置进行更改,可以使用 matplotlibrc 文件,也可以使用程序中的 rcParams dict。下面是 matplotlibrc 文件的一个示例:
font.family : serif
font.serif : Times, Palatino, New Century Schoolbook, Bookman, Computer Modern Roman
font.sans-serif : Helvetica, Avant Garde, Computer Modern Sans serif
font.cursive : Zapf Chancery
font.monospace : Courier, Computer Modern Typewriter
text.usetex : true
每个系列中的第一个有效字体是将要加载的字体。如果未指定字体,默认情况下将使用计算机现代字体。所有其他字体都是 Adobe 字体。泰晤士报和帕拉蒂诺都有自己的数学字体,而其他的 Adobe 衬线字体使用计算机现代数学字体。详见psnfss2e.pdf。
要使用 tex 并选择例如 Helvetica 作为默认字体,而不编辑 matplotlibrc,请使用:
#!python
from matplotlib import rc
rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']})
## for Palatino and other serif fonts use:
#rc('font',**{'family':'serif','serif':['Palatino']})
rc('text', usetex=True)
注意你需要在进口matplotlib.pylab
之前做这个。
下面是标准示例,tex_demo.py:
from matplotlib import rc
from matplotlib.numerix import arange, cos, pi
from pylab import figure, axes, plot, xlabel, ylabel, title, grid, savefig, show
rc('text', usetex=True)
figure(1)
ax = axes([0.1, 0.1, 0.8, 0.7])
t = arange(0.0, 1.0+0.01, 0.01)
s = cos(2*2*pi*t)+2
plot(t, s)
xlabel(r'\textbf{time (s)}')
ylabel(r'\textit{voltage (mV)}',fontsize=16)
title(r"\TeX\ is Number $\displaystyle\sum_{n=1}^\infty\frac{-e^{i\pi}}{2^n}$!", fontsize=16, color='r')
grid(True)
savefig('tex_demo')
show()
请注意,当 TeX/LaTeX 支持启用时,您可以混合文本和数学模式。不支持显示数学模式($$ e=mc^2 $$),但是添加命令(如 tex_demo.py 中)将产生相同的结果。
为了生成可以嵌入到新 LaTeX 文档中的封装 postscript 文件,matplotlib 的默认行为是提取输出,这将删除 LaTeX 使用的一些 postscript 运算符,这些运算符在 eps 文件中是非法的。这一步会产生一些用户可能无法接受的字体。一种解决方法是在 rc 设置中将 ps.distiller.res 设置为更高的值(可能是 6000)。更好的解决方法,需要[http://www.foolabs.com/xpdf/download.htmlxpdf]或[http://poppler.freedesktop.org/poppler](xpdf 的新后端)可以通过将 rc ps.usedistiller 设置更改为 xpdf 来激活。xpdf 替代方案生成的 postscript 文本可以在 Adobe Illustrator 中编辑,也可以在转换为 pdf 后搜索。
可能的问题
- 在 Windows 上,可能需要修改 PATH 环境变量来查找 latex、dvipng 和 ghostscript 可执行文件。这是通过转到控制面板,选择“系统”图标,选择“高级”选项卡,然后单击“环境变量”按钮来完成的(人们认为 Linux 很复杂。嘘。)选择 PATH 变量,并添加适当的目录。
- 使用 MiKTeX 与计算机现代字体,如果你得到奇数-Agg 和 PNG 的结果,去 MiKTeX/选项和更新你的格式文件
- 这些字体在屏幕上看起来很糟糕。你可能正在运行苹果操作系统,在苹果电脑上使用 dvipng 有一些有趣的事情。在 matplotlibrc 文件中设置为 True。
- 在 Ubuntu 和 Gentoo 上,基本的 texlive 安装没有附带 type1cm 包。您可能需要安装一些额外的软件包来获得与其他乳胶发行版捆绑在一起的所有好东西。
- 已经取得了一些进展,因此 Matplotlib 直接使用 dvi 文件进行文本布局。这允许 latex 用于 pdf 和 svg 后端以及*Agg 和 PS 后端的文本布局。未来,乳胶安装可能是唯一的外部依赖。
万一事情不顺利
- 试试
rm -r ~/.matplotlib/*cache
- 确保 LaTeX、dvipng 和 ghostscript 都在您的 PATH 上工作。
- 在启用详细模式的情况下运行您的脚本:python example . py–verbose-help(或–verbose-debug-烦人)并检查输出。很可能那里报告了一个问题。如果您需要帮助,可以发布一个简短的例子来重现这种行为,明确指出对 rc 设置的任何更改,以及您安装了什么版本的 matplotlib、您的操作系统和–verbose-*输出。
附件
宫六世
宫六世
玛雅维冲浪
玛雅维冲浪
如果你想绘制一个表面表示矩阵的高程和颜色的点,你必须转换矩阵数据的三维数据!MayaVi2 可以理解。[:Cookbook/MayaVi/mlab:mlab]知道如何做到这一点,但它没有的漂亮的用户界面!MayaVi2。这是一个创建!SurfRegular 对象,然后把它加载进去!MayaVi2。该脚本的更详细版本在示例页面中给出。
import numpy
def f(x, y):
return numpy.sin(x*y)/(x*y)
x = numpy.arange(-7., 7.05, 0.1)
y = numpy.arange(-5., 5.05, 0.05)
from enthought.tvtk.tools import mlab
s = mlab.SurfRegular(x, y, f)
from enthought.mayavi.sources.vtk_data_source import VTKDataSource
d = VTKDataSource()
d.data = s.data
mayavi.add_source(d)
from enthought.mayavi.filters.warp_scalar import WarpScalar
w = WarpScalar()
mayavi.add_filter(w)
from enthought.mayavi.modules.outline import Outline
from enthought.mayavi.modules.surface import Surface
o = Outline()
s = Surface()
mayavi.add_module(o)
mayavi.add_module(s)
您可以通过运行“mayavi2 -n -x script.py”来运行这个脚本,通过菜单(File -> Open File)加载它,然后按 Ctrl+R,或者在 python shell 中输入“execfile('script.py ')。
附件
玛雅·提斯
玛雅·提斯
介绍
以下是如何最好地使用的一般提示!MayaVi2。
有效编写 MayaVi2 脚本
这里有一些提示,展示了如何交互式和有效地编写 mayavi2 脚本。
拖放对象
运行 contour.py python 脚本示例,您应该会得到:
首先,在“视图”菜单中点击 python,启用 python shell。底部应该会出现一个 python 外壳!MayaVi2 窗口:
然后从左侧的树视图中拖动任何对象,并将其放到 python shell 中,您将获得该对象。假设您想要获得大纲模块:
现在,您可以通过以下两种方式使用您的对象:直接在 python shell 中键入或者使用 explore()方法。
键入 python 外壳
您可以在 python shell 窗口中创建对象的实例。
请注意,在这个 python shell 中,您受益于“单词完成”,即一个小窗口弹出,让您选择期望的对象或方法的名称。
因此,您可以显示轮廓颜色的 RGB 值,例如:
然而,找出对象或方法可能并不那么容易:你可能不知道它们是如何相互依赖的。更简单的方法是使用 explore()方法。
使用 explore()方法
简单地打字
explore(_)
或者
explore(foobar)
如果您之前已经定义了它:
然后,您会看到以下窗口:
考虑最后一个例子,关于轮廓模块的颜色,您可以展开“树”,从而获得您需要的信息:
很厉害吧!😃
您也可以在 pyhon shell 中工作,创建您的对象等等...
在 python 外壳中工作
在嵌入的 Python 外壳上!MayaVi2 应用,名字‘may avi’是绑定的!MayaVi 脚本界面,可用于编写 mayavi 脚本,如下所示:
e = mayavi.engine # Get the MayaVi engine.
mayavi.new_scene() # Create a new scene
# [...]
mayavi.save_visualization('foo.mv2')
mayavi.load_visualization('foo.mv2')
请注意,mayavi 引擎允许您以强大的方式编写 Mayavi 脚本。例如:
e = mayavi.engine
这里的“e”是正在运行的引擎实例(mayavi/engine.py),其层次结构与您在 mayavi 树视图中看到的相同。例如:
e.scenes[0] # --> first scene in mayavi.
e.scenes[0].children[0] # --> first scene's first source (vtkfile)
另一个例子,只需运行 examples/contour.py 并键入/剪切/粘贴以下内容,即可获得标量剪切平面模块:
e = mayavi.engine
s = e.scenes[0].children[0].children[0].children[-1]
# Think scene.datafile.module_manager.last_module
可以从 IPython 内部使用 Mayavi2 并编写脚本。您必须首先使用“-wthread”命令行选项启动 IPython。这里有一个例子:
from enthought.mayavi.app import Mayavi
m = Mayavi()
m.main()
m.script.new_scene() # m.script is the mayavi.script.Script instance
engine = m.script.engine
在上图中,“m.script”与嵌入式 shell 上的“mayavi”实例相同。
如果您正在编写一个独立的脚本来可视化类似于示例/目录中的脚本,请使用任何示例作为模板。
保存快照
在 python 脚本中保存快照非常简单:
s = script.engine.current_scene
s.scene.save('test.jpg', size=(width,height))
你也可以用很多其他格式保存图片:!附言(ps),封装!PostScript (eps),PDF (pdf),位图(bmp),TIFF (tiff),PNG (png),!OpenInventor (iv),虚拟现实标记语言(wrl,vrml),Geomview (oogl),!渲染人肋骨(肋骨),波前(物体)。
保存快照的显而易见的必然结果是,为了拍电影而保存大量的快照,例如,没有!记录的每个快照的 MayaVi2 窗口弹出。
答案很简单(仅在 UN*X 框下):使用“X 虚拟帧缓冲区”。
以下几行给了你诀窍。当然,您可以通过用 shell、python 等编写脚本来改进它。
* create your X virtual framebuffer with the following command: 'xvfb :1 -screen 0 1280x1024x24'. It will use the display #1, with a size of 1280x1024 and 24 bit depth color;
* export your display: 'export DISPLAY=:1' (sh/bash syntax) or 'setenv DISPLAY :1' (csh/tcsh syntax)
* run your !MayaVi2 script as usual;
* once finished, and all your snapshots have been created, don't forget to kill the X virtual framebuffer and reset your display to its previous value. If not, you won't be able to see your movie ;-)
在颜色映射中启用 alpha 透明度
将模块管理器拖到 python shell 中,您将能够通过以下方式在颜色映射中启用 alpha 透明度:
dragged.scalar_lut_manager.lut.alpha_range=(0,1)
设置 MayaVi2 会话颜色
快跑!MayaVi2,转到“工具”菜单,然后“首选项”,然后“TVTK 场景”。
假设你想改变背景颜色:点击“背景颜色”标签。
在这里,你可以选择一个预定义的颜色,或者点击方块来设置你的 RGB 值。
此外,如果您想要设置前景色,它将应用于所有模块和过滤器,即轮廓颜色、文本颜色、标签轴等。
您的首选项将保存在!MayaVi2 配置文件,所以每次运行一个!MayaVi2 会话。
=使用 TVTK 写入 VTK 数据文件=
即将推出...
附件
玛雅维:运行玛雅维 2
玛雅维:运行玛雅维 2
介绍
!MayaVi2 使用两种“对象”:模块和过滤器。
!P. Ramachandran 的《MayaVi 用户指南》(第 1.5 节)对它们的定义如下:
* "A Module is an object that actually visualizes the data.";
* "A Filter is an object that filters out the data in some way or the other. A simple example is the !ExtractVectorNorm filter. This extracts the magnitude of the selected vector data field attribute for the data file. All modules that use this as an input will receive the vector magnitude as the scalar field data."
你可以跑了!MayaVi2 使用以下两种方式之一:
* run !MayaVi2 as is, without any options on the command line. You'll manage modules and/or filters using the GUI;
* run !MayaVi2 with some options on the command line, thus loading directly the modules and/or the filters you want to use.
按原样运行 MayaVi2
注意:为了工作,这里假设您的 PATH 变量包含目录!安装了 MayaVi2 二进制文件(例如,/usr/local/bin)。
开始跑!MayaVi2:
mayavi2
!MayaVi2 将推出一个空白的 TVTK 场景。
* Let's start by loading some data. Go into File/Open/Vtk file. This will bring up a file dialogue, use this to select the heart.vtk file under examples/data directory.
The TVTK Scene has not changed, but you see that the file is loaded by looking at the upper left window.
* Now that we have data, we can visualize it. First lets create an outline. Click on "VTK File (heart.vtk)" in the upper left window.
Go into the menu item Visualize/Modules/Outline. This creates an Outline object. We can edit the properties of the outline in the object editor, and we can rotate and manipulate the object in the scene editor.
* Now lets add three grid planes.
Select Visualize/Modules/!GridPlane, this create a grid plane in the X axis. We can create two more grid planes. By selecting Axis in object editor, we can set those grid planes in the Y and Z axis.
* We can project the object onto the Grid planes using contour grid planes. Select Visualize/Modules/Contour grid plane. Do this for the Y, and Z planes as well.
* Now we can look at our data by selecting Visualize/Modules/Isosurface.
* We can now finish adding a scalar cut plane by selecting Visualize/Modules/Scalar Cut Plane. By clicking on the white arrow we can move the plane around.
结果应该是这样的(有关背景/前景色的问题,请参阅[:Cookbook/MayaVi/Tips:Cookbook/MayaVi/Tips]):
[](文件/附件/MayaVi_RunningMayavi2/mv2.png
使用一些参数运行 MayaVi2:模块和过滤器可用
为了知道可以在命令行上设置哪些参数,请键入
mayavi2 -h
这将列出所有可用的选项。所有这些选项包括:
* -d file.vtk: set the VTK file to load into !MayaVi2;
* -m
module
: set the module to use. The following list displays all available modules, in alphabetical order with a short description (taken from Python doc files):
*
Axes
: draws a simple axes using tvtk.!CubeAxesActor;
*
!ContourGridPlane
: a contour grid plane module. This module lets one take a slice of input grid data and view contours of the data. The module only works for structured points, rectilinear grid and structured grid input.
*
!CustomGridPlane
: a custom grid plane with a lot more flexibility than !GridPlane. This also only works for non-unstructured/non-polygonal datasets.
*
Glyph
: displays different types of glyphs oriented and colored as per scalar or vector data at the input points;
*
!GridPlane
: a simple grid plane module;
*
!ImagePlaneWidget
: a simple !ImagePlaneWidget module to view image data;
*
!IsoSurface
: an !IsoSurface module that allows the user to make contours of input point data;
*
!OrientationAxes
: creates a small axes on the side that indicates the position of the co-ordinate axes and thereby marks the orientation of the scene. It uses the !OrientationMarkerWidget which requires VTK-4.5 and above.
*
Outline
: an outline module that draws an outline for the given data;
*
!ScalarCutPlane
: takes a cut plane of any input data set using an implicit plane and plots the data with optional contouring and scalar warping;
*
!SliceUnstructuredGrid
: this module takes a slice of the unstructured grid data and shows the cells that intersect or touch the slice;
*
Streamline
: allows the user to draw streamlines for given vector data. This supports various types of seed objects (line, sphere, plane and point seeds). It also allows the user to draw ribbons or tubes and further supports different types of interactive modes of calculating the streamlines.
*
!StructuredGridOutline
: draws a grid-conforming outline for structured grids;
*
Surface
: draws a surface for any input dataset with optional contouring;
*
Text
: this module allows the user to place text on the screen;
*
!VectorCutPlane
: takes an arbitrary slice of the input data using an implicit cut plane and places glyphs according to the vector field data. The glyphs may be colored using either the vector magnitude or the scalar attributes.
*
Vectors
: displays different types of glyphs oriented and colored as per vector data at the input points. This is merely a convenience module that is entirely based on the Glyph module.
*
Volume
: the Volume module visualizes scalar fields using volumetric visualization techniques. This supports !ImageData and !UnstructuredGrid data. It also supports the !FixedPointRenderer for !ImageData. However, the performance is slow so your best bet is probably with the !ImageData based renderers.
*
!WarpVectorCutPlane
: takes an arbitrary slice of the input data using an implicit cut plane and warps it according to the vector field data. The scalars are displayed on the warped surface as colors.
* -f
filter
: set the filter to use (load it before module if you want to see your data filtered). Available filters are:
*
!CellToPointData
: transforms cell attribute data to point data by averaging the cell data from the cells at the point.
*
Delaunay2D
: performs a 2D Delaunay triangulation using the tvtk.Delaunay2D class;
*
Delaunay3D
: performs a 3D Delaunay triangulation using the tvtk.Delaunay3D class;
*
!ExtractGrid
: allows a user to select a part of a structured grid;
*
!ExtractUnstructuredGrid
: allows a user to select a part of an unstructured grid;
*
!ExtractVectorNorm
: computes the norm (Eucliedean) of the input vector data (with optional scaling between [0, 1]). This is useful when the input data has vector input but no scalar data for the magnitude of the vectors.
*
!MaskPoints
: selectively passes the input points downstream. This can be used to subsample the input points. Note that this does not pass geometry data, this means all grid information is lost.
*
!PointToCellData
: does the inverse of the !CellToPointData filter;
*
!PolyDataNormals
: computes normals from input data. This gives meshes a smoother appearance. This should work for any input dataset. Note: this filter is called "Compute Normals" in !MayaVi2 GUI (Visualize/Filters/Compute Normals).
*
Threshold
: a simple filter that thresholds on input data;
*
!TransformData
: performs a linear transformation to input data using a tvtk.!BoxWidget. This does not work with !ImageData/StructuredPoints.
*
!WarpScalar
: warps the input data along a particular direction (either the normals or a specified direction) with a scale specified by the local scalar value. Useful for making carpet plots.
*
!WarpVector
: warps the input data along a the point vector attribute scaled as per a scale factor. Useful for showing flow profiles or displacements.
好吧,你认为你会很快厌倦键入所有这些长名称模块和过滤器吗?别担心,使用你的 shell 完成命令!
例如,对于(t)csh shell,您可以将这一行放在配置 shell 文件中:
complete mayavi2 c/-/"(3 d f m n M p q w x z)"/ n/-3/f:*.3ds/ n/-d/f:*.vt?/ n/-f/"(CellToPointData Delaunay2D Delaunay3D ExtractGrid ExtractUnstructuredGrid ExtractVectorNorm MaskPoints PointToCellData PolyDataNormals Threshold TransformData WarpScalar WarpVector)"/ n/-m/"(Axes ContourGridPlane CustomGridPlane Glyph GridPlane ImagePlaneWidget IsoSurface Outline OrientationAxes ScalarCutPlane SliceUnstructuredGrid Streamline StructuredGridOutline Surface Text Vectors VectorCutPlane Volume WarpVectorCutPlane)"/ n/-p/f:{*xyz.bin,*.xyz}/ n/-q/f:{*q.bin,*.q}/ n/-w/f:*.wrl/ n/-x/f:*.py/ n/-z/f:*.mv2/
第一次输入相当长的时间,但是一旦输入,就不再需要加载您想要使用的模块或过滤器了!;-)
于是,打字(在!MayaVi2 的示例目录,参见[:cook book/may avi/Installation:cook book/may avi/Installation]]:
mayavi2 -d data/heart.vtk -m Outline -m GridPlane -m GridPlane -m GridPlane -m ContourGridPlane -m ContourGridPlane -m IsoSurface -m ScalarCutPlane
你应该明白:
好的,这与上图不完全相同,尽管加载了完全相同的模块。
原因是你没有设置(也不能,AFAIK)一些模块属性,比如 iso-value for!Iso 曲面模块,法线到!GridPlane 等。
希望您可以使用图形用户界面“手动”设置这些参数。
那么现在,让我们来玩这个 GUI-)
四处走动
所以,你可以在这两幅图上看到右边的渲染窗口(TVTK 场景)在左边的模块树旁边。让我们考虑第一个数字。
您可以像往常一样使用 OpenGL 使用鼠标处理渲染场景:
- 按住左键移动鼠标旋转场景;
- 按下中间按钮移动鼠标可以平移它;
- 按住右键移动鼠标可以放大/缩小(注意:如果你有鼠标的话,你也可以使用它的滚轮)。
注意:你可以通过点击“视图”项或每个小图标获得一个“预定义”的视角(垂直于 X 轴、y 轴或 z 轴)(第一个 X: Ox 轴指向你的后方,第二个 X: Ox 轴指向你的方向,等等...)
在窗口左侧可以看到加载了哪些模块(“TVTK Scene”是场景的“根”,“VTK 文件”是数据源,即 heart.vtk 文件):
- “轮廓”模块显示场景周围的方框;
- 您有三个网格平面("!GridPlane "模块),在坐标 x = 0、y = 0 和 z = 0 处;
- 两个轮廓网格平面("!ContourGridPlane "模块):第一个显示等高线(垂直方向),第二个,z = const 处的剖切面;
- "!等值面”模块将心脏显示为黄色;
- 最后一个剖切面(垂直方向,y = const)由"!ScalarCutPlane”模块。
请注意,这些场景中没有使用滤镜。
使用每个模块/过滤器非常直观。单击树中的模块/过滤器,用鼠标设置一些参数,或者在图形用户界面左下方的窗口中用键盘输入一些值。
如果您想要复制/粘贴/删除/剪切给定的模块/过滤器,请用右键单击它。一个小窗口弹出,有一个项目列表。
注意:双击树中选择的模块/过滤器可以得到一个更大的窗口。
要加载其他模块或添加过滤器,请单击窗口顶部的“可视化”项目。
最后,你还可以加载另一个 VTK 数据文件,加载/保存场景在一个"!MayaVi2 "文件(扩展名为. mv2),或者将其转换为所需的图像格式(PNG、JPEG...),单击“文件”项目或相应的图标(小软盘)。
你也可以通过点击小方块中代表四个红色箭头的小图标来全屏显示你的场景。要禁用全屏模式,请键入“e”或“q”。
这是最简单的使用方法!MayaVi2。这里提醒您,您也可以尝试“mayavi2 -h”来查看可以添加到!MayaVi2 命令行。
附件
编写 Mayavi 2 脚本
编写 Mayavi 2 脚本
|| 本页展示了使用高级、面向对象的 API 编写 Mayavi2 脚本。Mayavi2 最近获得了一个易于使用的脚本模块:mlab,虽然它的功能可能不那么强大。请参考美亚威 2 用户指南的部分。阅读此页面将让您更深入地了解 Mayavi2 的工作原理,它是对用户指南的补充。||
介绍
去写剧本!MayaVi2,你需要(至少):
* your favorite text editor;``* python installed ;-)``* !MayaVi2 installed ;-)
脚本!MayaVi2 相当简单,因为!MayaVi2 是用 python 编写的,基于 TVTK,它简化了所有 VTK 对象的使用。
在下面,您将学习如何编写脚本和使用!MayaVi2 模块和过滤器。
模块可以分为两部分:
* modules which do not interact with VTK data, and are seldom modified/handled (Outline, Axes, !OrientationAxes and Text). These are called the "basic" modules. Although color bar is not strictly speaking a module, it will be presented here. Setting a few parameters for rendering your scene will be also presented.
* modules which do interact with VTK data, and those you want to play with (i.e. all the remainder modules ;-).
开始之前,我们先来看看 a 的“主模板”!用 python 编写的 MayaVi2 脚本。
主模板:创建你的 MayaVi2 类
a!MayaVi2 脚本应该至少包含以下几行:
#! /usr/bin/env python
from enthought.mayavi.app import Mayavi
class MyClass(Mayavi):
def run(self):
script = self.script
# `self.script` is the MayaVi Script interface (an instance of
# enthought.mayavi.script.Script) that is created by the
# base `Mayavi` class. Here we save a local reference for
# convenience.
## import any Mayavi modules and filters you want (they must be done here!)
.../...
script.new_scene() # to create the rendering scene
## your stuff (modules, filters, etc) here
.../...
if __name__ == '__main__':
mc = MyClass()
mc.main()
添加模块或过滤器非常简单:您必须导入它,然后将其添加到您的!MayaVi2 类。
要添加模块,请键入:
from enthought.mayavi.modules.foo_module import FooModule
.../...
mymodule = FooModule()
script.add_module(mymodule)
要添加过滤器,请键入:
from enthought.mayavi.filters.bar_filter import BarFilter
.../...
myfilter = BarFilter()
script.add_filter(myfilter)
请注意使用的语法:例如,对于模块,foo_module 是 foo_module python 文件(没有扩展名。py)在 mayavi/目录的子目录 module/中(小写,下划线);该文件包含类 FooModule(无下划线,大写名称)。
但是首先,在用您想要使用的模块和过滤器渲染场景之前,您当然必须加载一些数据。
正在加载数据
你可以选择:
* create a 3D data array, for scalars data (for vectors data, you have to use a 4D scalars data, i.e. a 3D scalar data for each component) and load it with !ArraySource method;
* load a data file with !FileReader methods.
使用 ArraySource 方法从数组加载数据
例如,我们将创建余弦&正弦函数乘积的 50 50 50 3D(标量)数组。
为此,我们需要加载适当的模块:
import scipy
from scipy import ogrid, sin, cos, sqrt, pi
from enthought.mayavi.sources.array_source import ArraySource
Nx = 50
Ny = 50
Nz = 50
Lx = 1
Ly = 1
Lz = 1
x, y, z = ogrid[0:Lx:(Nx+1)*1j,0:Ly:(Ny+1)*1j,0:Lz:(Nz+1)*1j]
# Strictly speaking, H is the magnetic field of the "transverse electric" eigenmode m=n=p=1
# of a rectangular resonator cavity, with dimensions Lx, Ly, Lz
Hx = sin(x*pi/Lx)*cos(y*pi/Ly)*cos(z*pi/Lz)
Hy = cos(x*pi/Lx)*sin(y*pi/Ly)*cos(z*pi/Lz)
Hz = cos(x*pi/Lx)*cos(y*pi/Ly)*sin(z*pi/Lz)
Hv_scal = sqrt(Hx**2 + Hy**2 + Hz**2)
# We want to load a scalars data (Hv_scal) as magnitude of a given 3D vector (Hv = {Hx, Hy, Hz})
# Hv_scal is a 3D scalars data, Hv is a 4D scalars data
src = ArraySource()
src.scalar_data = Hv_scal # load scalars data
# To load vectors data
# src.vector_data = Hv
使用文件阅读器方法从文件加载数据
要加载 VTK 数据文件,比如 mayavi/examples/data/ directory 中的 heart.vtk 文件,只需键入:
from enthought.mayavi.sources.vtk_file_reader import VTKFileReader
src = VTKFileReader()
src.initialize("heart.vtk")
注意:文件带有。vtk 扩展名被称为“传统 VTK”文件。!MayaVi2 可以读取很多其他文件格式(XML、来自 Ensight、Plot3D 等的文件)。例如,您可以加载一个 XML 文件(扩展名为。vti,。vtp,。vtr,。vts,。vtu 等)使用 VTKXML!FileReader 方法。
将源代码添加到您的 MayaVi2 类中
然后,一旦使用上述两种方法之一加载了数据,就在类的主体中使用 add_source()方法添加源!MyClass(在 script.new_scene 之后):
script.add_source(src)
四个基本模块轮廓,轴,!现在将呈现方向轴和文本。
基本模块
请参见[:cook book/MayaVi/scriptingmayavi 2/Basic Modules:Basic Modules]维基页面。
主要模块
请参见[:cook book/MayaVi/scriptingmayavi 2/Main Modules:Main Modules]维基页面。
过滤
请参见[:Cookbook/MayaVi/scriptingmayavi 2/Filters:Filters]维基页面。
十、Mayavi / TVTK
玛比:姆拉迪
玛比:姆拉迪
|| 本页是关于 tvtk.tools.mlab 模块的。强烈建议您使用更新的 mayavi.mlab 模块,该模块也可以从 ipython -wthread 中使用,或者作为另一个应用中的库使用。||
mlab.py
模块允许简单的物体三维绘图。它为三维可视化提供了一种面向对象的方法。
它依赖于简单的 TVTK 模块,而不是成熟的 Mayavi 应用。因此,它具有较少的依赖性。然而,它更难扩展,也更受限制。开发人员不打算在该模块中添加任何功能,我们强烈建议您使用 Mayavi mlab 模块,该模块提供相同的用例,但功能更多,并且正在不断开发中。更多信息可以阅读美亚威用户指南相关章节
''目录'''
一个简单的例子
|| 重要提示:所有这些示例都必须在“ipython -wthread”或 Wx 应用(如 py 地壳或 Mayavi2 应用)中运行。如果不使用此选项,它们将不起作用。||
从ipython -wthread
开始,粘贴如下代码:
.. code:: python
导入 scipy
准备一些有趣的函数:def f(x,y):
返回 3.0scipy . sin(xy+1e-4)/(x * y+1e-4)
x = scipy . arang(-7.7 . 05 . 0 . 1)y = scipy . arang(-5.5 . 05 . 0 . 1)
f 的 3D 可视化:来自 entsure . tvtk . tools import mlab fig = mlab . fig()s = mlab。表面规则(x,y,f)图添加
[](文件/附件/Mayavi _ mlab/simple _ example . png
改变轴和颜色
from scipy import *
[x,y]=mgrid[-5:5:0.1,-5:5:0.1]
r=sqrt(x**2+y**2)
z=sin(3*r)/(r)
from enthought.tvtk.tools import mlab
# Open a viewer without the object browser:
f=mlab.figure(browser=False)
s=mlab.Surf(x,y,z,z)
f.add(s)
s.scalar_bar.title='sinc(r)'
s.show_scalar_bar=True
# LUT means "Look-Up Table", it give the mapping between scalar value and color
s.lut_type='blue-red'
# The current figure has two objects, the outline object originaly present,
# and the surf object that we added.
f.objects[0].axis.z_label='value'
t=mlab.Title()
t.text='Sampling function'
f.add(t)
# Edit the title properties with the GUI:
t.edit_traits()
[](文件/附件/MaYavi _ mlab/tvtk . mlab _ example . png
不同功能的列表
这里提供的实现是面向对象的,每个可视化功能都被实现为一个具有特性的类。所以这些都可以配置。每个可视化类(最终)从 MLabBase 派生,MLabBase 负责在渲染窗口中添加/移除其参与者。这些类都要求渲染窗口是一个pyface.tvtk.scene.Scene
实例(如果以后有必要,这个约束可以放宽)。
本模块提供以下广泛的功能:
*
Figure
This basically manages all of the objects rendered. Just like`` figure in any Matlab like environment. A convenience function`` called
【图】may be used to create a nice Figure instance.``*
This and its subclasses let one place glyphs at points specified`` as inputs. The subclasses are:
【箭】,
【锥】,
【立方】, ```py 气缸
,`
**要查看所有这些的好例子,请看这个文件末尾的test_*
函数。下面是一个使用这些测试函数的快速示例:
from enthought.tvtk.tools import mlab
f = mlab.figure()
mlab.test_surf(f) # Create a spherical harmonic.
f.pop() # Remove it.
mlab.test_molecule(f) # Show a caffeine molecule.
f.renwin.reset_zoom() # Scale the view.
f.pop() # Remove this.
mlab.test_lines(f) # Show pretty lines.
f.clear() # Remove all the stuff on screen.
附件
玛雅瓦 tvtk
玛雅瓦 tvtk
|| 此页面不是文档的主要来源。请参考 Mayavi2 主页了解 TVTK 的最新文档。特别要记住的是,如果你正在寻找一个高级别的 Python 3D 绘图库,Mayavi 也提供了合适的 API,并且可以嵌入(参见用户指南)。||**
十一、NumPy 和 SciPy / 高级主题
NumPy 中的视图与副本
NumPy 中的视图与副本
不时有人写信给!NumPy 列表询问在哪些情况下创建了数组视图,在哪些情况下没有创建。这一页试图澄清这个相当微妙的问题上的一些棘手之处。
什么是 NumPy 数组的视图?
顾名思义,这只是查看数组数据的另一种方式。从技术上讲,这意味着两个对象的数据是共享的。您可以通过选择原始数组的一个切片或通过更改数据类型(或两者的组合)来创建视图。这些不同类型的视图描述如下。
切片视图
这可能是中最常见的视图创作来源!NumPy。创建切片视图的经验法则是,可以用原始数组中的偏移量、步长和计数来处理被查看的元素。例如:
>>> a = numpy.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> v1 = a[1:2]
>>> v1
array([1])
>>> a[1] = 2
>>> v1
array([2])
>>> v2 = a[1::3]
>>> v2
array([2, 4, 7])
>>> a[7] = 10
>>> v2
array([ 2, 4, 10])
在上面的代码片段中,()和()是的视图,因为如果更新,将会反映更改。
数据类型视图
创建数组视图的另一种方法是将另一个数据类型分配给同一数据区域。例如:
>>> b = numpy.arange(10, dtype='int16')
>>> b
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int16)
>>> v3 = b.view('int32')
>>> v3 += 1
>>> b
array([1, 1, 3, 3, 5, 5, 7, 7, 9, 9], dtype=int16)
>>> v4 = b.view('int8')
>>> v4
array([1, 0, 1, 0, 3, 0, 3, 0, 5, 0, 5, 0, 7, 0, 7, 0, 9, 0, 9, 0], dtype=int8)
在这种情况下,和是的视图。如您所见,数据类型视图不如切片视图有用,但在某些情况下会很方便(例如,快速查看泛型数组的字节)。
常见问题解答
我想我明白什么是视图,但是为什么花哨的索引不返回视图呢?
人们可能会想,做花哨的索引可能会导致视图切片。但这不是真的:
>>> a = numpy.arange(10)
>>> c1 = a[[1,3]]
>>> c2 = a[[3,1,1]]
>>> a[:] = 100
>>> c1
array([1, 3])
>>> c2
array([3, 1, 1])
花式索引不返回视图的原因是,一般来说,它不能表示为一个切片(在上述意义上,可以用偏移量、步长和计数来处理)。
例如,的花式索引可以用来表示,但是不能用切片的方式来表示。所以,这就是为什么会返回一个带有原始数据的副本的对象。
但是花哨的索引有时确实会返回视图,不是吗?
许多用户被愚弄了,当他们使用这个习惯用法时,他们认为花哨的索引返回视图而不是副本:
>>> a = numpy.arange(10)
>>> a[[1,2]] = 100
>>> a
array([ 0, 100, 100, 3, 4, 5, 6, 7, 8, 9])
所以,看起来一个<1,2>实际上是一个视图,因为元素 1 和 2 已经更新了。然而,如果我们一步一步地尝试,这是行不通的:
>>> a = numpy.arange(10)
>>> c1 = a[[1,2]]
>>> c1[:] = 100
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> c1
array([100, 100])
这里发生的是,在第一个习惯用法()中,python 解释器将其翻译为:
a.__setitem__([1,2], 100)
即不需要创建视图或副本,因为该方法可以在处进行评估(即不涉及新对象的创建)。
然而,第二个成语()被翻译成:
c1 = a.__getitem__([1,2])
c1.__setitem__(slice(None, None, None), 100) # [:] translates into slice(None, None, None)
即在调用之前创建并返回一个新对象,该对象的某些元素具有副本(记住,花式索引不返回视图)。
这里的经验法则可以是:在左值索引的上下文中(即索引放在赋值的左侧值),不创建数组的视图或副本(因为不需要)。但是,对于常规值,上述创建视图的规则确实适用。
最后的练习
最后,让我们提出一个有些高级的问题。下一个片段如预期的那样工作:
>>> a = numpy.arange(12).reshape(3,4)
>>> ifancy = [0,2]
>>> islice = slice(0,3,2)
>>> a[islice, :][:, ifancy] = 100
>>> a
array([[100, 1, 100, 3],
[ 4, 5, 6, 7],
[100, 9, 100, 11]])
但是下一个没有:
>>> a = numpy.arange(12).reshape(3,4)
>>> ifancy = [0,2]
>>> islice = slice(0,3,2)
>>> a[ifancy, :][:, islice] = 100 # note that ifancy and islice are interchanged here
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
读者会发现为什么会有差异吗?' '提示:根据{{ getitem ()}}和{{{ setitem ()}}调用的顺序以及它们对每个示例的作用来思考。''
十二、Numpy 和 Scipy / 插值
插入文字
插入文字
在科学信号中使用 B 样条
演示如何在 scipy.signal 中使用 B 样条进行插值的示例。输入点必须等距才能使用这些例程。
from numpy import r_, sin
from scipy.signal import cspline1d, cspline1d_eval
%pylab inline
x = r_[0:10]
dx = x[1]-x[0]
newx = r_[-3:13:0.1] # notice outside the original domain
y = sin(x)
cj = cspline1d(y)
newy = cspline1d_eval(cj, newx, dx=dx,x0=x[0])
from pylab import plot, show
plot(newx, newy, x, y, 'o')
show()
Populating the interactive namespace from numpy and matplotlib
等间距数据的二维插值
scipy.ndimage 包还包含 spline_filter 和 map _ 坐标,可用于对等间距数据执行 N 维插值。下面给出了一个二维例子:
from scipy import ogrid, sin, mgrid, ndimage, array
from matplotlib import pyplot as plt
x,y = ogrid[-1:1:5j,-1:1:5j]
fvals = sin(x)*sin(y)
newx,newy = mgrid[-1:1:100j,-1:1:100j]
x0 = x[0,0]
y0 = y[0,0]
dx = x[1,0] - x0
dy = y[0,1] - y0
ivals = (newx - x0)/dx
jvals = (newy - y0)/dy
coords = array([ivals, jvals])
newf1 = ndimage.map_coordinates(fvals, coords)
要预先计算权重(对于多个插值结果),您可以使用
coeffs = ndimage.spline_filter(fvals)
newf2 = ndimage.map_coordinates(coeffs, coords, prefilter=False)
plt.subplot(1,2,1)
plt.imshow(newf1)
plt.subplot(1,2,2)
plt.imshow(newf2)
plt.show()
一维曲线的插值
scipy.interpolate 软件包包装了 netlib FITPACK 例程(Dierckx),用于计算各种数据和几何图形的平滑样条。虽然在这个例子中数据是均匀分布的,但是使用这个例程并不需要如此。
from numpy import arange, cos, linspace, pi, sin, random
from scipy.interpolate import splprep, splev
# make ascending spiral in 3-space
t=linspace(0,1.75*2*pi,100)
x = sin(t)
y = cos(t)
z = t
# add noise
x+= random.normal(scale=0.1, size=x.shape)
y+= random.normal(scale=0.1, size=y.shape)
z+= random.normal(scale=0.1, size=z.shape)
# spline parameters
s=3.0 # smoothness parameter
k=2 # spline order
nest=-1 # estimate of number of knots needed (-1 = maximal)
# find the knot points
tckp,u = splprep([x,y,z],s=s,k=k,nest=-1)
# evaluate spline, including interpolated points
xnew,ynew,znew = splev(linspace(0,1,400),tckp)
import pylab
pylab.subplot(2,2,1)
data,=pylab.plot(x,y,'bo-',label='data')
fit,=pylab.plot(xnew,ynew,'r-',label='fit')
pylab.legend()
pylab.xlabel('x')
pylab.ylabel('y')
pylab.subplot(2,2,2)
data,=pylab.plot(x,z,'bo-',label='data')
fit,=pylab.plot(xnew,znew,'r-',label='fit')
pylab.legend()
pylab.xlabel('x')
pylab.ylabel('z')
pylab.subplot(2,2,3)
data,=pylab.plot(y,z,'bo-',label='data')
fit,=pylab.plot(ynew,znew,'r-',label='fit')
pylab.legend()
pylab.xlabel('y')
pylab.ylabel('z')
plt.show()
使用径向基函数进行平滑/插值
使用径向基函数进行平滑/插值
径向基函数可用于平滑/插值 n 维中的分散数据,但应谨慎用于观测数据范围之外的外推。
1d 示例
这个例子比较了 scipy.interpolate 模块中 Rbf 和 UnivariateSpline 类的用法。
import numpy as np
from scipy.interpolate import Rbf, InterpolatedUnivariateSpline
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
# setup data
x = np.linspace(0, 10, 9)
y = np.sin(x)
xi = np.linspace(0, 10, 101)
# use fitpack2 method
ius = InterpolatedUnivariateSpline(x, y)
yi = ius(xi)
plt.subplot(2, 1, 1)
plt.plot(x, y, 'bo')
plt.plot(xi, yi, 'g')
plt.plot(xi, np.sin(xi), 'r')
plt.title('Interpolation using univariate spline')
# use RBF method
rbf = Rbf(x, y)
fi = rbf(xi)
plt.subplot(2, 1, 2)
plt.plot(x, y, 'bo')
plt.plot(xi, yi, 'g')
plt.plot(xi, np.sin(xi), 'r')
plt.title('Interpolation using RBF - multiquadrics')
plt.savefig('rbf1d.png')
[](文件/附件/RadialBasisFunctions/rbf1 dnew . png
十三、Numpy 和 Scipy / 线性代数
矩阵的秩和零空间
矩阵的秩和零空间
下面的模块 rank_nullspace.py 提供了函数 rank()和 nullspace()。(注意!NumPy 已经提供了函数 matrix _ rank();这里给出的函数允许指定绝对公差和相对公差。)
rank _ zero space . py
#!python
import numpy as np
from numpy.linalg import svd
def rank(A, atol=1e-13, rtol=0):
"""Estimate the rank (i.e. the dimension of the nullspace) of a matrix.
The algorithm used by this function is based on the singular value
decomposition of `A`.
Parameters
----------
A : ndarray
A should be at most 2-D. A 1-D array with length n will be treated
as a 2-D with shape (1, n)
atol : float
The absolute tolerance for a zero singular value. Singular values
smaller than `atol` are considered to be zero.
rtol : float
The relative tolerance. Singular values less than rtol*smax are
considered to be zero, where smax is the largest singular value.
If both `atol` and `rtol` are positive, the combined tolerance is the
maximum of the two; that is::
tol = max(atol, rtol * smax)
Singular values smaller than `tol` are considered to be zero.
Return value
------------
r : int
The estimated rank of the matrix.
See also
--------
numpy.linalg.matrix_rank
matrix_rank is basically the same as this function, but it does not
provide the option of the absolute tolerance.
"""
A = np.atleast_2d(A)
s = svd(A, compute_uv=False)
tol = max(atol, rtol * s[0])
rank = int((s >= tol).sum())
return rank
def nullspace(A, atol=1e-13, rtol=0):
"""Compute an approximate basis for the nullspace of A.
The algorithm used by this function is based on the singular value
decomposition of `A`.
Parameters
----------
A : ndarray
A should be at most 2-D. A 1-D array with length k will be treated
as a 2-D with shape (1, k)
atol : float
The absolute tolerance for a zero singular value. Singular values
smaller than `atol` are considered to be zero.
rtol : float
The relative tolerance. Singular values less than rtol*smax are
considered to be zero, where smax is the largest singular value.
If both `atol` and `rtol` are positive, the combined tolerance is the
maximum of the two; that is::
tol = max(atol, rtol * smax)
Singular values smaller than `tol` are considered to be zero.
Return value
------------
ns : ndarray
If `A` is an array with shape (m, k), then `ns` will be an array
with shape (k, n), where n is the estimated dimension of the
nullspace of `A`. The columns of `ns` are a basis for the
nullspace; each element in numpy.dot(A, ns) will be approximately
zero.
"""
A = np.atleast_2d(A)
u, s, vh = svd(A)
tol = max(atol, rtol * s[0])
nnz = (s >= tol).sum()
ns = vh[nnz:].conj().T
return ns
这是一个演示脚本。
#!python
import numpy as np
from rank_nullspace import rank, nullspace
def checkit(a):
print "a:"
print a
r = rank(a)
print "rank is", r
ns = nullspace(a)
print "nullspace:"
print ns
if ns.size > 0:
res = np.abs(np.dot(a, ns)).max()
print "max residual is", res
print "-"*25
a = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]])
checkit(a)
print "-"*25
a = np.array([[0.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]])
checkit(a)
print "-"*25
a = np.array([[0.0, 1.0, 2.0, 4.0], [1.0, 2.0, 3.0, 4.0]])
checkit(a)
print "-"*25
a = np.array([[1.0, 1.0j, 2.0+2.0j],
[1.0j, -1.0, -2.0+2.0j],
[0.5, 0.5j, 1.0+1.0j]])
checkit(a)
print "-"*25
这是脚本的输出。
-------------------------
a:
[[ 1\. 2\. 3.]
[ 4\. 5\. 6.]
[ 7\. 8\. 9.]]
rank is 2
nullspace:
[[-0.40824829]
[ 0.81649658]
[-0.40824829]]
max residual is 4.4408920985e-16
-------------------------
a:
[[ 0\. 2\. 3.]
[ 4\. 5\. 6.]
[ 7\. 8\. 9.]]
rank is 3
nullspace:
[]
-------------------------
a:
[[ 0\. 1\. 2\. 4.]
[ 1\. 2\. 3\. 4.]]
rank is 2
nullspace:
[[-0.48666474 -0.61177492]
[-0.27946883 0.76717915]
[ 0.76613356 -0.15540423]
[-0.31319957 -0.11409267]]
max residual is 3.88578058619e-16
-------------------------
a:
[[ 1.0+0.j 0.0+1.j 2.0+2.j ]
[ 0.0+1.j -1.0+0.j -2.0+2.j ]
[ 0.5+0.j 0.0+0.5j 1.0+1.j ]]
rank is 1
nullspace:
[[ 0.00000000-0.j -0.94868330-0.j ]
[ 0.13333333+0.93333333j 0.00000000-0.10540926j]
[ 0.20000000-0.26666667j 0.21081851-0.21081851j]]
max residual is 1.04295984227e-15
-------------------------
十四、Numpy 和 Scipy / Matplotlib 直方图
直方图
直方图
下面是一个创建可变仓位大小的 2D 直方图并显示的例子。
#!/usr/bin/python
import numpy as np
import matplotlib as ml
import matplotlib.pyplot as plt
# First we define the bin edges
xedges = [0, 1, 1.5, 3, 5]
yedges = [0, 2, 3, 4, 6]
# Next we create a histogram H with random bin content
x = np.random.normal(3, 1, 100)
y = np.random.normal(1, 1, 100)
H, xedges, yedges = np.histogram2d(y, x, bins=(xedges, yedges))
# Or we fill the histogram H with a determined bin content
H = np.ones((4, 4)).cumsum().reshape(4, 4)
print H[::-1] # This shows the bin content in the order as plotted
ml.rcParams['image.cmap'] = 'gist_heat'
fig = plt.figure(figsize=(6, 3.2))
# pcolormesh is useful for displaying exact bin edges
ax = fig.add_subplot(131)
ax.set_title('pcolormesh:\nexact bin edges')
X, Y = np.meshgrid(xedges, yedges)
plt.pcolormesh(X, Y, H)
ax.set_aspect('equal')
# NonUniformImage can be used for interpolation
ax = fig.add_subplot(132)
ax.set_title('NonUniformImage:\ninterpolated')
im = ml.image.NonUniformImage(ax, interpolation='bilinear')
xcenters = xedges[:-1] + 0.5 * (xedges[1:] - xedges[:-1])
ycenters = yedges[:-1] + 0.5 * (yedges[1:] - yedges[:-1])
im.set_data(xcenters, ycenters, H)
ax.images.append(im)
ax.set_xlim(xedges[0], xedges[-1])
ax.set_ylim(yedges[0], yedges[-1])
ax.set_aspect('equal')
# Imshow is useful for a simple equidistant representation of bin content
ax = fig.add_subplot(133)
ax.set_title('imshow:\nequitdistant')
im = plt.imshow(H, interpolation='nearest', origin='low',
extent=[xedges[0], xedges[-1], yedges[0], yedges[-1]])
# Finally we add a color bar
cax = fig.add_axes([0.12, 0.1, 0.78, 0.4])
cax.get_xaxis().set_visible(False)
cax.get_yaxis().set_visible(False)
cax.patch.set_alpha(0)
cax.set_frame_on(False)
plt.colorbar(orientation='horizontal', ax=cax)
plt.tight_layout()
plt.show()
[[ 13\. 14\. 15\. 16.]
[ 9\. 10\. 11\. 12.]
[ 5\. 6\. 7\. 8.]
[ 1\. 2\. 3\. 4.]]
附件
十五、Numpy 和 Scipy / 优化和拟合技术
- 拟合数据
- scipy大规模管束调整
- 最小二乘圆
- 线性回归
- ols
- 优化和拟合演示
- 优化演示
- transac
- scipy 中的稳健非线性回归
- 在 scipy 中求解离散边值问题
拟合数据
拟合数据
本页向您展示了如何使用 matplotlib 拟合实验数据和绘制结果。
用正弦函数拟合示例
生成数据
使用真实数据要有趣得多,但是,为了让您能够重现这个示例,我将生成适合的数据
import numpy as np
from numpy import pi, r_
import matplotlib.pyplot as plt
from scipy import optimize
# Generate data points with noise
num_points = 150
Tx = np.linspace(5., 8., num_points)
Ty = Tx
tX = 11.86*np.cos(2*pi/0.81*Tx-1.32) + 0.64*Tx+4*((0.5-np.random.rand(num_points))*np.exp(2*np.random.rand(num_points)**2))
tY = -32.14*np.cos(2*np.pi/0.8*Ty-1.94) + 0.15*Ty+7*((0.5-np.random.rand(num_points))*np.exp(2*np.random.rand(num_points)**2))
拟合数据
我们现在有两组数据:时间序列 tX 和 tY,以及带噪声的正弦数据 Tx 和 Ty。我们感兴趣的是找到正弦波的频率。
# Fit the first set
fitfunc = lambda p, x: p[0]*np.cos(2*np.pi/p[1]*x+p[2]) + p[3]*x # Target function
errfunc = lambda p, x, y: fitfunc(p, x) - y # Distance to the target function
p0 = [-15., 0.8, 0., -1.] # Initial guess for the parameters
p1, success = optimize.leastsq(errfunc, p0[:], args=(Tx, tX))
time = np.linspace(Tx.min(), Tx.max(), 100)
plt.plot(Tx, tX, "ro", time, fitfunc(p1, time), "r-") # Plot of the data and the fit
# Fit the second set
p0 = [-15., 0.8, 0., -1.]
p2,success = optimize.leastsq(errfunc, p0[:], args=(Ty, tY))
time = np.linspace(Ty.min(), Ty.max(), 100)
plt.plot(Ty, tY, "b^", time, fitfunc(p2, time), "b-")
# Legend the plot
plt.title("Oscillations in the compressed trap")
plt.xlabel("time [ms]")
plt.ylabel("displacement [um]")
plt.legend(('x position', 'x fit', 'y position', 'y fit'))
ax = plt.axes()
plt.text(0.8, 0.07,
'x freq : %.3f kHz \n y freq : %.3f kHz' % (1/p1[1],1/p2[1]),
fontsize=16,
horizontalalignment='center',
verticalalignment='center',
transform=ax.transAxes)
plt.show()
成本函数的巧妙运用
假设你有相同的数据集:两个时间序列的振荡现象,但你知道两个振荡的频率是相同的。成本函数的巧妙使用可以让您使用相同的频率在一次拟合中拟合两组数据。其思想是将两个数据集的成本串联起来作为一个“成本”数组返回给一个参数选择。因此,最小化例程同时优化两个数据集。
# Target function
fitfunc = lambda T, p, x: p[0]*np.cos(2*np.pi/T*x+p[1]) + p[2]*x
# Initial guess for the first set's parameters
p1 = r_[-15., 0., -1.]
# Initial guess for the second set's parameters
p2 = r_[-15., 0., -1.]
# Initial guess for the common period
T = 0.8
# Vector of the parameters to fit, it contains all the parameters of the problem, and the period of the oscillation is not there twice !
p = r_[T, p1, p2]
# Cost function of the fit, compare it to the previous example.
errfunc = lambda p, x1, y1, x2, y2: r_[
fitfunc(p[0], p[1:4], x1) - y1,
fitfunc(p[0], p[4:7], x2) - y2
]
# This time we need to pass the two sets of data, there are thus four "args".
p,success = optimize.leastsq(errfunc, p, args=(Tx, tX, Ty, tY))
time = np.linspace(Tx.min(), Tx.max(), 100) # Plot of the first data and the fit
plt.plot(Tx, tX, "ro", time, fitfunc(p[0], p[1:4], time),"r-")
# Plot of the second data and the fit
time = np.linspace(Ty.min(), Ty.max(),100)
plt.plot(Ty, tY, "b^", time, fitfunc(p[0], p[4:7], time),"b-")
# Legend the plot
plt.title("Oscillations in the compressed trap")
plt.xlabel("time [ms]")
plt.ylabel("displacement [um]")
plt.legend(('x position', 'x fit', 'y position', 'y fit'))
ax = plt.axes()
plt.text(0.8, 0.07,
'x freq : %.3f kHz' % (1/p[0]),
fontsize=16,
horizontalalignment='center',
verticalalignment='center',
transform=ax.transAxes)
<matplotlib.text.Text at 0x7fab996e5b90>
简化语法
尤其是在使用 fits 进行交互时,optimize.leastsq 的标准语法可能会变得很长。使用以下脚本可以简化您的生活:
import numpy as np
from scipy import optimize
class Parameter:
def __init__(self, value):
self.value = value
def set(self, value):
self.value = value
def __call__(self):
return self.value
def fit(function, parameters, y, x = None):
def f(params):
i = 0
for p in parameters:
p.set(params[i])
i += 1
return y - function(x)
if x is None: x = np.arange(y.shape[0])
p = [param() for param in parameters]
return optimize.leastsq(f, p)
现在拟合变得非常容易,例如拟合高斯:
# giving initial parameters
mu = Parameter(7)
sigma = Parameter(3)
height = Parameter(5)
# define your function:
def f(x): return height() * np.exp(-((x-mu())/sigma())**2)
# fit! (given that data is an array with the data to fit)
data = 10*np.exp(-np.linspace(0, 10, 100)**2) + np.random.rand(100)
print fit(f, [mu, sigma, height], data)
(array([ -1.7202343 , 12.29906459, 10.74194291]), 1)
拟合高斯型数据
计算分布的矩
拟合高斯形状的数据不需要优化程序。仅仅计算分布的矩就足够了,而且这要快得多。
然而,这只有在高斯没有被切掉太多,并且不是太小的情况下才起作用。
gaussian = lambda x: 3*np.exp(-(30-x)**2/20.)
data = gaussian(np.arange(100))
plt.plot(data, '.')
X = np.arange(data.size)
x = np.sum(X*data)/np.sum(data)
width = np.sqrt(np.abs(np.sum((X-x)**2*data)/np.sum(data)))
max = data.max()
fit = lambda t : max*np.exp(-(t-x)**2/(2*width**2))
plt.plot(fit(X), '-')
[<matplotlib.lines.Line2D at 0x7fab9977d990>]
拟合 2D 高斯
这是适合 2D 高斯分布的健壮代码。它计算数据的矩来猜测优化例程的初始参数。对于更完整的高斯,一个可选的加性常数和旋转,请参见。它还允许指定已知的错误。
def gaussian(height, center_x, center_y, width_x, width_y):
"""Returns a gaussian function with the given parameters"""
width_x = float(width_x)
width_y = float(width_y)
return lambda x,y: height*np.exp(
-(((center_x-x)/width_x)**2+((center_y-y)/width_y)**2)/2)
def moments(data):
"""Returns (height, x, y, width_x, width_y)
the gaussian parameters of a 2D distribution by calculating its
moments """
total = data.sum()
X, Y = np.indices(data.shape)
x = (X*data).sum()/total
y = (Y*data).sum()/total
col = data[:, int(y)]
width_x = np.sqrt(np.abs((np.arange(col.size)-y)**2*col).sum()/col.sum())
row = data[int(x), :]
width_y = np.sqrt(np.abs((np.arange(row.size)-x)**2*row).sum()/row.sum())
height = data.max()
return height, x, y, width_x, width_y
def fitgaussian(data):
"""Returns (height, x, y, width_x, width_y)
the gaussian parameters of a 2D distribution found by a fit"""
params = moments(data)
errorfunction = lambda p: np.ravel(gaussian(*p)(*np.indices(data.shape)) -
data)
p, success = optimize.leastsq(errorfunction, params)
return p
这里有一个使用它的例子:
# Create the gaussian data
Xin, Yin = np.mgrid[0:201, 0:201]
data = gaussian(3, 100, 100, 20, 40)(Xin, Yin) + np.random.random(Xin.shape)
plt.matshow(data, cmap=plt.cm.gist_earth_r)
params = fitgaussian(data)
fit = gaussian(*params)
plt.contour(fit(*np.indices(data.shape)), cmap=plt.cm.copper)
ax = plt.gca()
(height, x, y, width_x, width_y) = params
plt.text(0.95, 0.05, """
x : %.1f
y : %.1f
width_x : %.1f
width_y : %.1f""" %(x, y, width_x, width_y),
fontsize=16, horizontalalignment='right',
verticalalignment='bottom', transform=ax.transAxes)
<matplotlib.text.Text at 0x7fab9d8a4dd0>
用幂律拟合有误差的数据
生成数据
生成一些带噪声的数据来演示拟合过程。生成的数据振幅为 10,幂律指数为-2.0。请注意,当记录日志时,我们所有的数据都表现良好...对于真实数据,您可能需要更加小心。
# Define function for calculating a power law
powerlaw = lambda x, amp, index: amp * (x**index)
##########
# Generate data points with noise
##########
num_points = 20
# Note: all positive, non-zero data
xdata = np.linspace(1.1, 10.1, num_points)
ydata = powerlaw(xdata, 10.0, -2.0) # simulated perfect data
yerr = 0.2 * ydata # simulated errors (10%)
ydata += np.random.randn(num_points) * yerr # simulated noisy data
拟合数据
如果你的数据表现良好,你可以通过对数转换成线性方程来拟合幂律函数。然后使用优化功能拟合一条直线。请注意,我们是通过拟合过程中的位置不确定性来加权的。此外,最佳拟合参数的不确定性是根据方差-协方差矩阵估计的。当使用这种形式的误差估计不合适时,你应该仔细阅读。如果你试图拟合一个幂律分布,这个解更合适。
##########
# Fitting the data -- Least Squares Method
##########
# Power-law fitting is best done by first converting
# to a linear equation and then fitting to a straight line.
#
# y = a * x^b
# log(y) = log(a) + b*log(x)
#
logx = np.log10(xdata)
logy = np.log10(ydata)
logyerr = yerr / ydata
# define our (line) fitting function
fitfunc = lambda p, x: p[0] + p[1] * x
errfunc = lambda p, x, y, err: (y - fitfunc(p, x)) / err
pinit = [1.0, -1.0]
out = optimize.leastsq(errfunc, pinit,
args=(logx, logy, logyerr), full_output=1)
pfinal = out[0]
covar = out[1]
print pfinal
print covar
index = pfinal[1]
amp = 10.0**pfinal[0]
indexErr = np.sqrt( covar[0][0] )
ampErr = np.sqrt( covar[1][1] ) * amp
##########
# Plotting data
##########
plt.clf()
plt.subplot(2, 1, 1)
plt.plot(xdata, powerlaw(xdata, amp, index)) # Fit
plt.errorbar(xdata, ydata, yerr=yerr, fmt='k.') # Data
plt.text(5, 6.5, 'Ampli = %5.2f +/- %5.2f' % (amp, ampErr))
plt.text(5, 5.5, 'Index = %5.2f +/- %5.2f' % (index, indexErr))
plt.title('Best Fit Power Law')
plt.xlabel('X')
plt.ylabel('Y')
plt.xlim(1, 11)
plt.subplot(2, 1, 2)
plt.loglog(xdata, powerlaw(xdata, amp, index))
plt.errorbar(xdata, ydata, yerr=yerr, fmt='k.') # Data
plt.xlabel('X (log scale)')
plt.ylabel('Y (log scale)')
plt.xlim(1.0, 11)
[ 1.00341313 -2.00447676]
[[ 0.01592265 -0.0204523 ]
[-0.0204523 0.03027352]]
(1.0, 11)
附件
scipy 中的大规模束调整
scipy 中的大规模束调整
三维重建中出现束调整问题,可表述如下(摘自https://en.wikipedia.org/wiki/Bundle_adjustment):
给定一组描绘来自不同视点的多个 3D 点的图像,束调整可以被定义为根据涉及所有点的对应图像投影的最优性标准,同时细化描述场景几何形状以及用于获取图像的摄像机的相对运动和光学特性的参数的 3D 坐标的问题。
更准确地说。我们在现实世界中有一组点,它们的坐标\((X, Y, Z)\)
定义在一些先验选择的“世界坐标框架”中。我们用不同的相机拍摄这些点,相机的特征是它们相对于世界坐标系的方向和平移,以及焦距和两个径向失真参数(总共 9 个参数)。然后我们精确地测量由摄像机投射到图像上的点的二维坐标\((x, y)\)
。我们的任务是通过最小化重投影误差的平方和来细化原始点的三维坐标以及相机参数。
让\(\pmb{P} = (X, Y, Z)^T\)
-一个点的半径向量,\(\pmb{R}\)
-一个摄像机的旋转矩阵,\(\pmb{t}\)
-一个摄像机的平移向量,\(f\)
-它的焦距,\(k_1, k_2\)
-它的畸变参数。然后重新投影如下进行:
\[\begin{align} \pmb{Q} = \pmb{R} \pmb{P} + \pmb{t} \\ \pmb{q} = -\begin{pmatrix} Q_x / Q_z \\ Q_y / Q_z \end{pmatrix} \\ \pmb{p} = f (1 + k_1 \lVert \pmb{q} \rVert^2 + k_2 \lVert \pmb{q} \rVert^4) \pmb{q} \end{align}\]
得到的矢量\(\pmb{p}=(x, y)^T\)
包含原始点的图像坐标。这个模型叫做“针孔摄像头模型”,关于这个课题我在这里找到了一个很好的笔记http://www.comp.nus.edu.sg/~cs4243/lecture/camera.pdf
现在让我们开始解决一些真正的捆绑调整问题。我们将从http://grail.cs.washington.edu/projects/bal/来解决一个问题。
from __future__ import print_function
import urllib
import bz2
import os
import numpy as np
首先下载数据文件:
BASE_URL = "http://grail.cs.washington.edu/projects/bal/data/ladybug/"
FILE_NAME = "problem-49-7776-pre.txt.bz2"
URL = BASE_URL + FILE_NAME
if not os.path.isfile(FILE_NAME):
urllib.request.urlretrieve(URL, FILE_NAME)
现在从文件中读取数据:
def read_bal_data(file_name):
with bz2.open(file_name, "rt") as file:
n_cameras, n_points, n_observations = map(
int, file.readline().split())
camera_indices = np.empty(n_observations, dtype=int)
point_indices = np.empty(n_observations, dtype=int)
points_2d = np.empty((n_observations, 2))
for i in range(n_observations):
camera_index, point_index, x, y = file.readline().split()
camera_indices[i] = int(camera_index)
point_indices[i] = int(point_index)
points_2d[i] = [float(x), float(y)]
camera_params = np.empty(n_cameras * 9)
for i in range(n_cameras * 9):
camera_params[i] = float(file.readline())
camera_params = camera_params.reshape((n_cameras, -1))
points_3d = np.empty(n_points * 3)
for i in range(n_points * 3):
points_3d[i] = float(file.readline())
points_3d = points_3d.reshape((n_points, -1))
return camera_params, points_3d, camera_indices, point_indices, points_2d
camera_params, points_3d, camera_indices, point_indices, points_2d = read_bal_data(FILE_NAME)
这里有 numpy 数组:
- 带有形状
(n_cameras, 9)
的camera_params
包含所有摄像机参数的初始估计。每行前 3 个分量形成一个旋转矢量(https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula),后 3 个分量形成一个平移矢量,然后是一个焦距和两个畸变参数。 points_3d
带形状(n_points, 3)
包含世界帧中点坐标的初始估计。- 带形状
(n_observations,)
的camera_ind
包含每次观察涉及的摄像机指数(从 0 到n_cameras - 1
)。 point_ind
形状(n_observations,)
包含每次观察涉及的点的指数(从 0 到n_points - 1
)。points_2d
带形状(n_observations, 2)
包含每次观测中投影到图像上的点的测量二维坐标。
这些数字是:
n_cameras = camera_params.shape[0]
n_points = points_3d.shape[0]
n = 9 * n_cameras + 3 * n_points
m = 2 * points_2d.shape[0]
print("n_cameras: {}".format(n_cameras))
print("n_points: {}".format(n_points))
print("Total number of parameters: {}".format(n))
print("Total number of residuals: {}".format(m))
n_cameras: 49
n_points: 7776
Total number of parameters: 23769
Total number of residuals: 63686
我们选择了一个相对较小的问题来减少计算时间,但是 scipy 的算法能够解决更大的问题,尽管所需的时间会成比例地增长。
现在定义返回残差向量的函数。我们使用 numpy 矢量化计算:
def rotate(points, rot_vecs):
"""Rotate points by given rotation vectors.
Rodrigues' rotation formula is used.
"""
theta = np.linalg.norm(rot_vecs, axis=1)[:, np.newaxis]
with np.errstate(invalid='ignore'):
v = rot_vecs / theta
v = np.nan_to_num(v)
dot = np.sum(points * v, axis=1)[:, np.newaxis]
cos_theta = np.cos(theta)
sin_theta = np.sin(theta)
return cos_theta * points + sin_theta * np.cross(v, points) + dot * (1 - cos_theta) * v
def project(points, camera_params):
"""Convert 3-D points to 2-D by projecting onto images."""
points_proj = rotate(points, camera_params[:, :3])
points_proj += camera_params[:, 3:6]
points_proj = -points_proj[:, :2] / points_proj[:, 2, np.newaxis]
f = camera_params[:, 6]
k1 = camera_params[:, 7]
k2 = camera_params[:, 8]
n = np.sum(points_proj**2, axis=1)
r = 1 + k1 * n + k2 * n**2
points_proj *= (r * f)[:, np.newaxis]
return points_proj
def fun(params, n_cameras, n_points, camera_indices, point_indices, points_2d):
"""Compute residuals.
`params` contains camera parameters and 3-D coordinates.
"""
camera_params = params[:n_cameras * 9].reshape((n_cameras, 9))
points_3d = params[n_cameras * 9:].reshape((n_points, 3))
points_proj = project(points_3d[point_indices], camera_params[camera_indices])
return (points_proj - points_2d).ravel()
你可以看到计算fun
的雅可比是很麻烦的,因此我们将依赖于有限差分近似。为了使这个处理时间可行,我们提供雅可比稀疏结构(即,标记已知非零的元素):
from scipy.sparse import lil_matrix
def bundle_adjustment_sparsity(n_cameras, n_points, camera_indices, point_indices):
m = camera_indices.size * 2
n = n_cameras * 9 + n_points * 3
A = lil_matrix((m, n), dtype=int)
i = np.arange(camera_indices.size)
for s in range(9):
A[2 * i, camera_indices * 9 + s] = 1
A[2 * i + 1, camera_indices * 9 + s] = 1
for s in range(3):
A[2 * i, n_cameras * 9 + point_indices * 3 + s] = 1
A[2 * i + 1, n_cameras * 9 + point_indices * 3 + s] = 1
return A
现在我们准备运行优化。让我们可视化用初始参数评估的残差。
%matplotlib inline
import matplotlib.pyplot as plt
x0 = np.hstack((camera_params.ravel(), points_3d.ravel()))
f0 = fun(x0, n_cameras, n_points, camera_indices, point_indices, points_2d)
plt.plot(f0)
[<matplotlib.lines.Line2D at 0x1067696d8>]
A = bundle_adjustment_sparsity(n_cameras, n_points, camera_indices, point_indices)
import time
from scipy.optimize import least_squares
t0 = time.time()
res = least_squares(fun, x0, jac_sparsity=A, verbose=2, x_scale='jac', ftol=1e-4, method='trf',
args=(n_cameras, n_points, camera_indices, point_indices, points_2d))
t1 = time.time()
Iteration Total nfev Cost Cost reduction Step norm Optimality
0 1 8.5091e+05 8.57e+06
1 3 5.0985e+04 8.00e+05 1.46e+02 1.15e+06
2 4 1.6077e+04 3.49e+04 2.59e+01 2.43e+05
3 5 1.4163e+04 1.91e+03 2.86e+02 1.21e+05
4 7 1.3695e+04 4.67e+02 1.32e+02 2.51e+04
5 8 1.3481e+04 2.14e+02 2.24e+02 1.54e+04
6 9 1.3436e+04 4.55e+01 3.18e+02 2.73e+04
7 10 1.3422e+04 1.37e+01 6.84e+01 2.20e+03
8 11 1.3418e+04 3.70e+00 1.28e+02 7.88e+03
9 12 1.3414e+04 4.19e+00 2.64e+01 6.24e+02
10 13 1.3412e+04 1.88e+00 7.55e+01 2.61e+03
11 14 1.3410e+04 2.09e+00 1.77e+01 4.97e+02
12 15 1.3409e+04 1.04e+00 4.02e+01 1.32e+03
ftol termination condition is satisfied.
Function evaluations 15, initial cost 8.5091e+05, final cost 1.3409e+04, first-order optimality 1.32e+03.
print("Optimization took {0:.0f} seconds".format(t1 - t0))
Optimization took 33 seconds
设置scaling='jac'
是为了自动缩放变量并均衡它们对成本函数的影响(很明显,摄像机参数和点的坐标是非常不同的实体)。事实证明,该选项对于成功的捆绑调整至关重要。
现在让我们绘制找到的解的残差图:
plt.plot(res.fun)
[<matplotlib.lines.Line2D at 0x10de73438>]
我们现在看到了更好的残差图像,平均值非常接近于零。还剩下一些尖刺。这可以用数据中的异常值来解释,也可能是算法找到了一个局部极小值(虽然是一个非常好的极小值)或者没有收敛到足够的程度。请注意,该算法使用雅可比有限差分近似,这可能会由于精度不足而阻碍接近最小值的过程(但是,计算该问题的精确雅可比相当困难)。
最小二乘圆
最小二乘圆
介绍
本页收集了用于寻找拟合一组 2D 点(x,y)的最小二乘圆的不同方法。
此分析的完整代码可在此获得: least_squares_circle_v1d.py
。
寻找最小二乘圆对应于寻找圆的中心(xc,yc)及其半径 Rc,其最小化下面定义的剩余函数:
#! python
Ri = sqrt( (x - xc)**2 + (y - yc)**2)
residu = sum( (Ri - Rc)**2)
这是一个非线性问题。我们可以看到解决这个问题的三种方法,并比较它们的结果和速度。
使用代数近似
如本文件所述,如果我们将函数定义为如下最小化,则该问题可以近似为线性问题:
#! python
residu_2 = sum( (Ri**2 - Rc**2)**2)
这导致使用 linalg.solve 的以下方法:
#! python
# == METHOD 1 ==
method_1 = 'algebraic'
# coordinates of the barycenter
x_m = mean(x)
y_m = mean(y)
# calculation of the reduced coordinates
u = x - x_m
v = y - y_m
# linear system defining the center (uc, vc) in reduced coordinates:
# Suu * uc + Suv * vc = (Suuu + Suvv)/2
# Suv * uc + Svv * vc = (Suuv + Svvv)/2
Suv = sum(u*v)
Suu = sum(u**2)
Svv = sum(v**2)
Suuv = sum(u**2 * v)
Suvv = sum(u * v**2)
Suuu = sum(u**3)
Svvv = sum(v**3)
# Solving the linear system
A = array([ [ Suu, Suv ], [Suv, Svv]])
B = array([ Suuu + Suvv, Svvv + Suuv ])/2.0
uc, vc = linalg.solve(A, B)
xc_1 = x_m + uc
yc_1 = y_m + vc
# Calcul des distances au centre (xc_1, yc_1)
Ri_1 = sqrt((x-xc_1)**2 + (y-yc_1)**2)
R_1 = mean(Ri_1)
residu_1 = sum((Ri_1-R_1)**2)
使用 scipy.optimize.leastsq
Scipy 将提供几种工具来解决上述非线性问题。其中scipy . optimize . lestsq在这种情况下使用起来非常简单。
事实上,一旦定义了圆心,半径就可以直接计算出来,并等于平均值(Ri)。所以只剩下两个参数:xc 和 yc。
基本用法
#! python
# == METHOD 2 ==
from scipy import optimize
method_2 = "leastsq"
def calc_R(xc, yc):
""" calculate the distance of each 2D points from the center (xc, yc) """
return sqrt((x-xc)**2 + (y-yc)**2)
def f_2(c):
""" calculate the algebraic distance between the data points and the mean circle centered at c=(xc, yc) """
Ri = calc_R(*c)
return Ri - Ri.mean()
center_estimate = x_m, y_m
center_2, ier = optimize.leastsq(f_2, center_estimate)
xc_2, yc_2 = center_2
Ri_2 = calc_R(*center_2)
R_2 = Ri_2.mean()
residu_2 = sum((Ri_2 - R_2)**2)
高级用法,带雅可比函数
为了提高速度,可以告诉 optimize.leastsq 如何通过添加一个 Dfun 参数来计算函数的雅可比:
#! python
# == METHOD 2b ==
method_2b = "leastsq with jacobian"
def calc_R(xc, yc):
""" calculate the distance of each data points from the center (xc, yc) """
return sqrt((x-xc)**2 + (y-yc)**2)
def f_2b(c):
""" calculate the algebraic distance between the 2D points and the mean circle centered at c=(xc, yc) """
Ri = calc_R(*c)
return Ri - Ri.mean()
def Df_2b(c):
""" Jacobian of f_2b
The axis corresponding to derivatives must be coherent with the col_deriv option of leastsq"""
xc, yc = c
df2b_dc = empty((len(c), x.size))
Ri = calc_R(xc, yc)
df2b_dc[0] = (xc - x)/Ri # dR/dxc
df2b_dc[1] = (yc - y)/Ri # dR/dyc
df2b_dc = df2b_dc - df2b_dc.mean(axis=1)[:, newaxis]
return df2b_dc
center_estimate = x_m, y_m
center_2b, ier = optimize.leastsq(f_2b, center_estimate, Dfun=Df_2b, col_deriv=True)
xc_2b, yc_2b = center_2b
Ri_2b = calc_R(*center_2b)
R_2b = Ri_2b.mean()
residu_2b = sum((Ri_2b - R_2b)**2)
使用 scipy.odr
Scipy 有一个处理正交距离回归的专用包,即 scipy.odr 。这个包可以处理显式和隐式函数定义,在这种情况下,我们将使用第二种形式。
这是圆的隐含定义:
#! python
(x - xc)**2 + (y-yc)**2 - Rc**2 = 0
基本用法
这将导致以下代码:
#! python
# == METHOD 3 ==
from scipy import odr
method_3 = "odr"
def f_3(beta, x):
""" implicit definition of the circle """
return (x[0]-beta[0])**2 + (x[1]-beta[1])**2 -beta[2]**2
# initial guess for parameters
R_m = calc_R(x_m, y_m).mean()
beta0 = [ x_m, y_m, R_m]
# for implicit function :
# data.x contains both coordinates of the points (data.x = [x, y])
# data.y is the dimensionality of the response
lsc_data = odr.Data(row_stack([x, y]), y=1)
lsc_model = odr.Model(f_3, implicit=True)
lsc_odr = odr.ODR(lsc_data, lsc_model, beta0)
lsc_out = lsc_odr.run()
xc_3, yc_3, R_3 = lsc_out.beta
Ri_3 = calc_R([xc_3, yc_3])
residu_3 = sum((Ri_3 - R_3)**2)
高级用法,带有雅可比函数
隐函数定义的优点之一是它的导数很容易计算。
这可用于完成模型:
#! python
# == METHOD 3b ==
method_3b = "odr with jacobian"
def f_3b(beta, x):
""" implicit definition of the circle """
return (x[0]-beta[0])**2 + (x[1]-beta[1])**2 -beta[2]**2
def jacb(beta, x):
""" Jacobian function with respect to the parameters beta.
return df_3b/dbeta
"""
xc, yc, r = beta
xi, yi = x
df_db = empty((beta.size, x.shape[1]))
df_db[0] = 2*(xc-xi) # d_f/dxc
df_db[1] = 2*(yc-yi) # d_f/dyc
df_db[2] = -2*r # d_f/dr
return df_db
def jacd(beta, x):
""" Jacobian function with respect to the input x.
return df_3b/dx
"""
xc, yc, r = beta
xi, yi = x
df_dx = empty_like(x)
df_dx[0] = 2*(xi-xc) # d_f/dxi
df_dx[1] = 2*(yi-yc) # d_f/dyi
return df_dx
def calc_estimate(data):
""" Return a first estimation on the parameter from the data """
xc0, yc0 = data.x.mean(axis=1)
r0 = sqrt((data.x[0]-xc0)**2 +(data.x[1] -yc0)**2).mean()
return xc0, yc0, r0
# for implicit function :
# data.x contains both coordinates of the points
# data.y is the dimensionality of the response
lsc_data = odr.Data(row_stack([x, y]), y=1)
lsc_model = odr.Model(f_3b, implicit=True, estimate=calc_estimate, fjacd=jacd, fjacb=jacb)
lsc_odr = odr.ODR(lsc_data, lsc_model) # beta0 has been replaced by an estimate function
lsc_odr.set_job(deriv=3) # use user derivatives function without checking
lsc_odr.set_iprint(iter=1, iter_step=1) # print details for each iteration
lsc_out = lsc_odr.run()
xc_3b, yc_3b, R_3b = lsc_out.beta
Ri_3b = calc_R(xc_3b, yc_3b)
residu_3b = sum((Ri_3b - R_3b)**2)
三种方法的比较
我们将在两种情况下比较这三种方法的结果:
* when 2D points are all around the circle
* when 2D points are in a small arc
圆圈周围的数据点
下面是一个数据点环绕圆圈的例子:
#! python
# Coordinates of the 2D points
x = r_[ 9, 35, -13, 10, 23, 0]
y = r_[ 34, 10, 6, -14, 27, -10]
这给出了:
| | | | | | | | | | | | | | | |SUMMARY| | | |Method|Xc|Yc|Rc|nb _ calls|STD(Ri)|残差|残差 2 ||代数| | 10 . 555
注意:
*
nb_calls correspond to the number of function calls of the function to be minimized, and do not take into account the number of calls to derivatives function. This differs from the number of iteration as ODR can make multiple calls during an iteration.
* as shown on the figures below, the two functions
废弃物 and
residu_2 are not equivalent, but their minima are close in this case.
圆弧周围的数据点
下面是一个数据点形成弧形的例子:
#! python
x = r_[36, 36, 19, 18, 33, 26]
y = r_[14, 10, 28, 31, 18, 26]
''方法''' | ' ' Xc ' ' ' | ' ' Yc ' ' ' | ' ' Rc ' ' ' | ' ' nb_calls ' ' ' | ''标准(Ri)' ' ' | ''残留物''' | ''剩余 2 ' ' ' |
---|---|---|---|---|---|---|---|
代数的 | 15.05503 | 8.83615 | 20.82995 | one | 0.930508 | 5.195076 | Nine thousand one hundred and fifty-three point four |
最小 q | 9.88760 | 3.68753 | 27.35040 | Twenty-four | 0.820825 | 4.042522 | Twelve thousand and one point nine eight |
雅可比最小二乘法 | 9.88759 | 3.68752 | 27.35041 | Ten | 0.820825 | 4.042522 | Twelve thousand and one point nine eight |
odr | 9.88757 | 3.68750 | 27.35044 | Four hundred and seventy-two | 0.820825 | 4.042522 | Twelve thousand and two point zero one |
带雅可比的 odr | 9.88757 | 3.68750 | 27.35044 | One hundred and nine | 0.820825 | 4.042522 | Twelve thousand and two point zero one |
结论
ODR 和莱斯特给出了同样的结果。
Optimize.leastsq 是最有效的方法,至少在函数调用次数上比 ODR 快两到十倍。
增加一个计算雅可比的函数可以使函数调用的数量减少 2 到 5 倍。
在这种情况下,使用 ODR 似乎有点矫枉过正,但是对于像省略号这样的更复杂的用例来说,它可能非常方便。
当点都在圆的周围时,代数近似给出了很好的结果,但是当只有一个弧要拟合时,代数近似就受到了限制。
事实上,当数据点不完全在一个圆上时,两个最小化误差的函数是不等价的。在大多数情况下,代数方法比最小二乘圆的半径小,因为它的误差函数是基于平方距离,而不是距离本身。
附件
arc_residu2_v1.png
arc_residu2_v2.png
arc_residu2_v3.png
arc_residu2_v4.png
arc_residu2_v5.png
arc_residu2_v6.png
arc_v1.png
arc_v2.png
arc_v3.png
arc_v4.png
arc_v5.png
full_cercle_residu2_v1.png
full_cercle_residu2_v2.png
full_cercle_residu2_v3.png
full_cercle_residu2_v4.png
full_cercle_residu2_v5.png
full_cercle_v1.png
full_cercle_v2.png
full_cercle_v3.png
full_cercle_v4.png
full_cercle_v5.png
least_squares_circle.py
least_squares_circle_v1.py
least_squares_circle_v1b.py
least_squares_circle_v1c.py
least_squares_circle_v1d.py
least_squares_circle_v2.py
least_squares_circle_v3.py
线性回归
线性回归
线性回归示例
from scipy import linspace, polyval, polyfit, sqrt, stats, randn
from pylab import plot, title, show , legend
#Linear regression example
# This is a very simple example of using two scipy tools
# for linear regression, polyfit and stats.linregress
#Sample data creation
#number of points
n=50
t=linspace(-5,5,n)
#parameters
a=0.8; b=-4
x=polyval([a,b],t)
#add some noise
xn=x+randn(n)
#Linear regressison -polyfit - polyfit can be used other orders polys
(ar,br)=polyfit(t,xn,1)
xr=polyval([ar,br],t)
#compute the mean square error
err=sqrt(sum((xr-xn)**2)/n)
print('Linear regression using polyfit')
print('parameters: a=%.2f b=%.2f \nregression: a=%.2f b=%.2f, ms error= %.3f' % (a,b,ar,br,err))
#matplotlib ploting
title('Linear Regression Example')
plot(t,x,'g.--')
plot(t,xn,'k.')
plot(t,xr,'r.-')
legend(['original','plus noise', 'regression'])
show()
#Linear regression using stats.linregress
(a_s,b_s,r,tt,stderr)=stats.linregress(t,xn)
print('Linear regression using stats.linregress')
print('parameters: a=%.2f b=%.2f \nregression: a=%.2f b=%.2f, std error= %.3f' % (a,b,a_s,b_s,stderr))
另一个例子:[http://www2 . warwick . AC . uk/fac/sci/moac/current students/Peter _ cock/python/Lin _ reg使用 scipy(和 R)计算线性回归】
附件
内源性阿片样物质
内源性阿片样物质
OLS 是普通最小二乘法的缩写。
该类估计多变量回归模型,并提供各种拟合统计。要查看正在运行的类,请下载 ols.py
文件并运行它。这)#将使用模拟数据估计多变量回归并提供输出。如果您安装了 rpy(http://rpy.sourceforge.net/),它还将提供来自 R 的输出来验证结果。
要导入该类,请执行以下操作:
#!python
import ols
导入类后,您可以通过向模型传递数据来估计模型,如下所示
#!python
mymodel = ols.ols(y,x,y_varnm,x_varnm)
其中 y 是包含因变量数据的数组,x 包含自变量,y_varnm 是包含因变量变量标签的字符串,x_varnm 是自变量变量标签的列表。注意:截取项和变量标签会自动添加到模型中。
示例用法
#!python
>>> import ols
>>> from numpy.random import randn
>>> data = randn(100,5)
>>> y = data[:,0]
>>> x = data[:,1:]
>>> mymodel = ols.ols(y,x,'y',['x1','x2','x3','x4'])
>>> mymodel.p # return coefficient p-values
array([ 0.31883448, 0.7450663 , 0.95372471, 0.97437927, 0.09993078])
>>> mymodel.summary() # print results
==============================================================================
Dependent Variable: y
Method: Least Squares
Date: Thu, 28 Feb 2008
Time: 22:32:24
# obs: 100
# variables: 5
==============================================================================
variable coefficient std. Error t-statistic prob.
==============================================================================
const 0.107348 0.107121 1.002113 0.318834
x1 -0.037116 0.113819 -0.326100 0.745066
x2 0.006657 0.114407 0.058183 0.953725
x3 0.003617 0.112318 0.032201 0.974379
x4 0.186022 0.111967 1.661396 0.099931
==============================================================================
Models stats Residual stats
==============================================================================
R-squared 0.033047 Durbin-Watson stat 2.012949
Adjusted R-squared -0.007667 Omnibus stat 5.664393
F-statistic 0.811684 Prob(Omnibus stat) 0.058883
Prob (F-statistic) 0.520770 JB stat 6.109005
Log likelihood -145.182795 Prob(JB) 0.047146
AIC criterion 3.003656 Skew 0.327103
BIC criterion 3.133914 Kurtosis 4.018910
==============================================================================
注意
库函数[http://docs . scipy . org/doc/numpy/reference/generated/numpy . linalg . lstsq . htmlnumpy . linalg . lstsq()]进行基本的 OLS 估计。
附件
优化和安装演示
优化和安装演示
这是一个从几个贝塞尔函数创建数据并找到局部最大值,然后使用来自scipy . interpole模块的一些样条函数拟合曲线的快速示例。恩图克包和 wxpython 用于创建剧情。py 地壳(wxpython 附带)被用作 python 外壳。
from enthought.chaco.wx import plt
from scipy import arange, optimize, special
plt.figure()
plt.hold()
w = []
z = []
x = arange(0,10,.01)
for k in arange(1,5,.5):
y = special.jv(k,x)
plt.plot(x,y)
f = lambda x: -special.jv(k,x)
x_max = optimize.fminbound(f,0,6)
w.append(x_max)
z.append(special.jv(k,x_max))
plt.plot(w,z, 'ro')
from scipy import interpolate
t = interpolate.splrep(w, z, k=3)
s_fit3 = interpolate.splev(x,t)
plt.plot(x,s_fit3, 'g-')
t5 = interpolate.splrep(w, z, k=5)
s_fit5 = interpolate.splev(x,t5)
plt.plot(x,s_fit5, 'y-')
#class left
# ![](files/attachments/OptimizationAndFitDemo1/chacoscreenshot.png
Optimization Example
附件
优化演示
优化演示
SciPy 的优化包是 scipy.optimize。最基本的非线性优化函数是:
* optimize.fmin(func, x0), which finds the minimum of func(x) starting x with x0 (x can be a vector)
* optimize.fsolve(func, x0), which finds a solution to func(x) = 0 starting with x = x0 (x can be a vector)
* optimize.fminbound(func, x1, x2), which finds the minimum of a scalar function func(x) for the range [x1,x2] (x1,x2 must be a scalar and func(x) must return a scalar)
详见 scipy.optimze 文档。
这是从几个贝塞尔函数生成数据并使用 fminbound 找到一些局部最大值的快速演示。这将 ipython 与-pylab 开关配合使用。
from scipy import optimize, special
from numpy import *
from pylab import *
x = arange(0,10,0.01)
for k in arange(0.5,5.5):
y = special.jv(k,x)
plot(x,y)
f = lambda x: -special.jv(k,x)
x_max = optimize.fminbound(f,0,6)
plot([x_max], [special.jv(k,x_max)],'ro')
title('Different Bessel functions and their local maxima')
show()
RANSAC
RANSAC
附件 ransac.py
实现 RANSAC 算法。示例图像:
要运行该文件,请将其保存到您的计算机上,启动 IPython
ipython -wthread
导入模块并运行测试程序
import ransac
ransac.test()
要使用该模块,您需要用两种方法创建一个模型类
def fit(self, data):
"""Given the data fit the data with your model and return the model (a vector)"""
def get_error(self, data, model):
"""Given a set of data and a model, what is the error of using this model to estimate the data """
这种模型的一个例子是类 LinearLeastSquaresModel,如源文件所示(如下)
附件
scipy 中的稳健非线性回归
scipy 中的稳健非线性回归
非线性最小二乘法的主要应用之一是非线性回归或曲线拟合。也就是说,通过给定对\(\left\{ (t_i, y_i) \: i = 1, \ldots, n \right\}\)
估计参数\(\mathbf{x}\)
,定义非线性函数\(\varphi(t; \mathbf{x})\)
,假设模型:
\[\begin{equation} y_i = \varphi(t_i; \mathbf{x}) + \epsilon_i \end{equation}\]
其中\(\epsilon_i\)
为测量(观察)误差。在最小二乘估计中,我们搜索\(\mathbf{x}\)
作为以下优化问题的解:
\[\begin{equation} \frac{1}{2} \sum_{i=1}^n (\varphi(t_i; \mathbf{x}) - y_i)^2 \rightarrow \min_\mathbf{x} \end{equation}\]
从数学角度来看,这种公式既直观又方便。从概率的角度来看,已知最小二乘解是最大似然估计,假设所有\(\epsilon_i\)
都是独立且正态分布的随机变量。
所以理论上当\(\epsilon_i\)
有非正态分布时不是最优的。尽管在工程实践中,这通常并不重要,即如果误差表现为一些合理的随机变量,且均值为零,则最小二乘估计的结果将是令人满意的。
真正的问题始于数据被异常值污染(完全错误的测量)。在这种情况下,最小二乘解会变得明显有偏差,以避免异常值的残差很高。为了定性地解释为什么会发生这种情况,让我们考虑最简单的最小二乘问题:
\[\begin{align} \frac{1}{2} \sum_{i=1}^n (a - x_i)^2 \rightarrow \min_a \end{align}\]
解是平均值:
\[\begin{align} a^* = \frac{1}{n} \sum_{i=1}^n x_i \end{align}\]
现在想象一下:\(a = 1, n=4, x_1 = 0.9, x_2 = 1.05, x_3=0.95, x_4=10 \text{ (outlier) }\)
。解决方案\(a^* = 3.225\)
,即完全被一个孤立点毁掉。
众所周知的稳健估计量之一是 l1 估计量,其中残差绝对值之和最小。为了演示,再次考虑最简单的问题:
\[\begin{equation} \sum_{i=1}^n |a - x_i| \rightarrow \min_a \end{equation}\]
而众所周知的解是\(\{x_i\}\)
的中值,这样少量的离群值不会影响解。在我们的玩具问题中,我们有\(a^* = 1\)
。
l1 估计器的唯一缺点是产生的优化问题是困难的,因为函数在任何地方都是不可微的,这对于有效的非线性优化是特别麻烦的。这意味着我们更好地处理可微的问题,但在估计中以某种方式结合稳健性。为了实现这一点,我们引入了一个次线性函数\(\rho(z)\)
(即它的增长应该慢于线性),并提出了一个新的类似最小二乘的优化问题:
\[\begin{equation} \frac{1}{2} \sum_{i=1}^n \rho \left((\varphi(t_i; x) - y_i)^2 \right) \rightarrow \min_x \end{equation}\]
结果表明,通过在每次迭代中修改残差向量和雅可比矩阵,可以将这个问题简化为标准的非线性最小二乘问题,这样计算出的梯度和 Hessian appxoximation 与目标函数的梯度和 Hessian appxoximation 相匹配。详见纸。
\(\rho\)
实现的选择如下:
- 给出标准最小二乘的线性函数:
\(\rho(z) = z\)
。 - 胡贝尔损失 :
\(\rho(z) = \begin{cases} z & z \leq 1 \\ \sqrt{z} - 1 & z > 1 \end{cases}\)
- 绝对值损失平滑逼近,“软 l1 损失”:
\(\rho(z) = 2 (\sqrt{1 + z} - 1)\)
- 柯西损失:
\(\rho(z) = \ln(1 + z)\)
。 - arctan 损失:
\(\rho(z) = \arctan z\)
。
函数 2 和 3 相对温和,对于较大的残差给出近似的绝对值损失。后两个函数是强次线性的,对异常值有显著的衰减。
上面的损失函数是在内联和外联之间的软阈值等于 1.0 的假设下编写的。概括地说,我们引入了比例参数\(C\)
并将损耗评估为
\[\begin{equation} \hat{\rho}(r^2) = C^2 \rho \left( \left(\frac{r}{C} \right)^2 \right) \end{equation}\]
请注意,如果残差很小,我们对上面定义的任何\(\rho(z)\)
都有\(\hat{\rho}(r^2) \approx \rho(r^2) \approx r^2\)
。
为了说明引入的函数的行为,我们构建了一个图:
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['figure.figsize'] = (10, 6)
rcParams['legend.fontsize'] = 16
rcParams['axes.labelsize'] = 16
r = np.linspace(0, 5, 100)
linear = r**2
huber = r**2
huber[huber > 1] = 2 * r[huber > 1] - 1
soft_l1 = 2 * (np.sqrt(1 + r**2) - 1)
cauchy = np.log1p(r**2)
arctan = np.arctan(r**2)
plt.plot(r, linear, label='linear')
plt.plot(r, huber, label='huber')
plt.plot(r, soft_l1, label='soft_l1')
plt.plot(r, cauchy, label='cauchy')
plt.plot(r, arctan, label='arctan')
plt.xlabel("$r$")
plt.ylabel(r"$\rho(r^2)$")
plt.legend(loc='upper left')
<matplotlib.legend.Legend at 0x1058c1ef0>
现在,我们将展示稳健的损失函数如何在一个模型例子中工作。我们将模型函数定义为
\[\begin{equation} f(t; A, \sigma, \omega) = A e^{-\sigma t} \sin (\omega t) \end{equation}\]
它可以模拟线性阻尼振荡器的观测位移。
定义数据生成器:
def generate_data(t, A, sigma, omega, noise=0, n_outliers=0, random_state=0):
y = A * np.exp(-sigma * t) * np.sin(omega * t)
rnd = np.random.RandomState(random_state)
error = noise * rnd.randn(t.size)
outliers = rnd.randint(0, t.size, n_outliers)
error[outliers] *= 35
return y + error
定义模型参数:
A = 2
sigma = 0.1
omega = 0.1 * 2 * np.pi
x_true = np.array([A, sigma, omega])
noise = 0.1
t_min = 0
t_max = 30
用于拟合参数的数据将包含 3 个异常值:
t_train = np.linspace(t_min, t_max, 30)
y_train = generate_data(t_train, A, sigma, omega, noise=noise, n_outliers=4)
定义最小二乘最小化的残差计算函数:
def fun(x, t, y):
return x[0] * np.exp(-x[1] * t) * np.sin(x[2] * t) - y
使用所有 1 作为初始估计。
x0 = np.ones(3)
from scipy.optimize import least_squares
运行标准最小二乘法:
res_lsq = least_squares(fun, x0, args=(t_train, y_train))
使用loss='soft_l1'
运行稳健最小二乘法,将f_scale
设置为 0.1,这意味着内联残差大约低于 0.1。
res_robust = least_squares(fun, x0, loss='soft_l1', f_scale=0.1, args=(t_train, y_train))
定义绘制全曲线的数据。
t_test = np.linspace(t_min, t_max, 300)
y_test = generate_data(t_test, A, sigma, omega)
使用找到的参数计算预测:
y_lsq = generate_data(t_test, *res_lsq.x)
y_robust = generate_data(t_test, *res_robust.x)
plt.plot(t_train, y_train, 'o', label='data')
plt.plot(t_test, y_test, label='true')
plt.plot(t_test, y_lsq, label='lsq')
plt.plot(t_test, y_robust, label='robust lsq')
plt.xlabel('$t$')
plt.ylabel('$y$')
plt.legend()
<matplotlib.legend.Legend at 0x107031898>
我们清楚地看到,标准最小二乘对异常值有显著反应,并给出非常有偏差的解,而稳健最小二乘(具有软 l1 损失)给出的解非常接近真实模型。
用 scipy 求解一个离散边值问题
用 scipy 求解一个离散边值问题
数学公式
我们考虑一个正方形区域内的非线性椭圆边值问题\(\Omega = [0, 1] \times [0, 1]\)
:
\[\begin{split}\Delta u + k f(u) = 0 \\ u = 0 \text{ on } \partial \Omega\end{split}\]
这里\(u = u(x,y)\)
是未知函数,\(\Delta\)
是拉普拉斯算子,\(k\)
是某常数,\(f(u)\)
是给定函数。
解决这类问题的一种常用计算方法是离散化。我们用统一的网格加上一些步骤\(h\)
,并定义\(u_{i, j} = u(i h , jh)\)
。通过使用 5 点有限差分逼近拉普拉斯算子,我们得到了如下形式的方程组:
\[\begin{split}u_{i - 1, j} + u_{i + 1, j} + u_{i, j - 1} + u_{i, j + 1} - 4 u_{i,j} + c f(u_{i,j}) = 0 \\ u_{i, j} = 0 \text{ on } \partial \Omega\end{split}\]
这里\(c = k h^2\)
。
为 scipy 定义问题
从现在开始,我们关注离散版本,考虑每个维度都有\(n = 100\)
记号的网格,并设置\(c = 1\)
和\(f(u) = u^3\)
。
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import least_squares
from scipy.sparse import coo_matrix
n = 100
c = 1
def f(u):
return u**3
def f_prime(u):
return 3 * u**2
为了求解方程组,我们将使用scipy.optimize.least_squares
。
我们定义了一个函数来计算每个方程的左边。请注意,我们假设边界上的值固定为零,并且在优化过程中不会改变它们。
def fun(u, n, f, f_prime, c, **kwargs):
v = np.zeros((n + 2, n + 2))
u = u.reshape((n, n))
v[1:-1, 1:-1] = u
y = v[:-2, 1:-1] + v[2:, 1:-1] + v[1:-1, :-2] + v[1:-1, 2:] - 4 * u + c * f(u)
return y.ravel()
如果可能,总是建议提供解析雅可比。在我们的问题中,我们有\(n^2=10000\)
方程和变量,但是每个方程只依赖于很少的变量,因此我们应该以稀疏的格式计算雅可比。
在雅可比矩阵中预先计算非零元素的行和列的索引是很方便的。我们定义了相应的函数:
def compute_jac_indices(n):
i = np.arange(n)
jj, ii = np.meshgrid(i, i)
ii = ii.ravel()
jj = jj.ravel()
ij = np.arange(n**2)
jac_rows = [ij]
jac_cols = [ij]
mask = ii > 0
ij_mask = ij[mask]
jac_rows.append(ij_mask)
jac_cols.append(ij_mask - n)
mask = ii < n - 1
ij_mask = ij[mask]
jac_rows.append(ij_mask)
jac_cols.append(ij_mask + n)
mask = jj > 0
ij_mask = ij[mask]
jac_rows.append(ij_mask)
jac_cols.append(ij_mask - 1)
mask = jj < n - 1
ij_mask = ij[mask]
jac_rows.append(ij_mask)
jac_cols.append(ij_mask + 1)
return np.hstack(jac_rows), np.hstack(jac_cols)
之后计算coo_matrix
格式的雅可比就简单了:
jac_rows, jac_cols = compute_jac_indices(n)
def jac(u, n, f, f_prime, c, jac_rows=None, jac_cols=None):
jac_values = np.ones_like(jac_cols, dtype=float)
jac_values[:n**2] = -4 + c * f_prime(u)
return coo_matrix((jac_values, (jac_rows, jac_cols)), shape=(n**2, n**2))
解决问题
在对问题没有任何洞察力的情况下,我们最初将所有值设置为 0.5。注意,不能保证给定的连续或离散问题有唯一的解。
u0 = np.ones(n**2) * 0.5
预计算雅可比矩阵中非零元素的行和列:
jac_rows, jac_cols = compute_jac_indices(n)
现在我们准备运行优化。第一个解的计算不会对变量施加任何限制。
res_1 = least_squares(fun, u0, jac=jac, gtol=1e-3, args=(n, f, f_prime, c), kwargs={'jac_rows': jac_rows, 'jac_cols': jac_cols}, verbose=1)
gtol termination condition is satisfied.
Function evaluations 106, initial cost 1.0412e+02, final cost 5.2767e-03, first-order optimality 9.04e-04.
下面我们设想第一个解决方案。左边的图显示了展平解决方案,中间的图显示了解决方案在方形域中的外观,右边的图显示了每个节点中的最终残差。
plt.figure(figsize=(16, 5))
plt.subplot(132)
plt.imshow(res_1.x.reshape((n, n)), cmap='coolwarm', vmin=-max(abs(res_1.x)), vmax=max(abs(res_1.x)))
plt.colorbar(use_gridspec=True, fraction=0.046, pad=0.04)
plt.subplot(131)
plt.plot(res_1.x)
plt.subplot(133)
plt.plot(res_1.fun)
plt.tight_layout()
可能有些物理因素要求解在任何地方都必须是非负的。我们可以通过指定求解器的边界来实现:
res_2 = least_squares(fun, u0, jac=jac, bounds=(0, np.inf), gtol=1e-3,
args=(n, f, f_prime, c), kwargs={'jac_rows': jac_rows, 'jac_cols': jac_cols}, verbose=1)
gtol termination condition is satisfied.
Function evaluations 34, initial cost 1.0412e+02, final cost 4.1342e-02, first-order optimality 9.55e-04.
plt.figure(figsize=(16, 5))
plt.subplot(132)
plt.imshow(res_2.x.reshape((n, n)), cmap='Reds')
plt.colorbar(use_gridspec=True, fraction=0.046, pad=0.04)
plt.subplot(131)
plt.plot(res_2.x)
plt.subplot(133)
plt.plot(res_2.fun)
plt.tight_layout()
我们看到,设置下限允许我们找到不同的非负解。
您可以尝试从不同的起点运行优化,使用不同的界限或更改\(c\)
和\(f(u)\)
,并从least_squares
解算器中查看它如何影响结果。
十六、Numpy 和 Scipy / 常微分方程
耦合弹簧-质量系统
耦合弹簧-质量系统
这个秘籍的例子展示了如何解决一个微分方程系统。(其他例子包括 【洛特卡-沃尔泰拉教程】【僵尸启示录】 和KdV example
)。)
弹簧-质量耦合系统
此图显示了要建模的系统:
质量为\(m_1\)
和\(m_2\)
的两个物体通过弹簧常数为\(k_1\)
和\(k_2\)
的弹簧耦合。左弹簧的左端是固定的。我们假设弹簧在不受外力作用时的长度为\(L_1\)
和\(L_2\)
。
质量在产生摩擦的表面上滑动,因此有两个摩擦系数,b,,1,,和 b,,2,…。
该系统的微分方程为
\(m_1 x_1'' + b_1 x_1' + k_1 (x_1 - L_1) - k_2 (x_2 - x_1 - L_2) = 0\)
\(m_2 x_2'' + b_2 x_2' + k_2 (x_2 - x_1 - L_2) = 0\)
这是一对耦合的二阶方程。要用 SciPy 提供的一个 ODE 解算器求解这个系统,我们必须首先把它转换成一个一阶微分方程系统。我们引入了两个变量
\(y_1 = x_1'\)
、\(y_2 = x_2'\)
这些是质量的速度。
用一点代数,我们可以把两个二阶方程改写成以下四个一阶方程的系统:
\(x_1' = y_1\)
\(y_1' = (-b_1 y_1 - k_1 (x_1 - L_1) + k_2 (x_2 - x_1 - L_2))/m_1\)
\(x_2' = y_2\)
\(y_2' = (-b_2 y_2 - k_2 (x_2 - x_1 - L_2))/m_2\)
这些方程现在是我们可以用 Python 实现的形式。
下面的代码定义了方程组(也称为矢量场)的“右手边”。我选择将定义向量场的函数放在它自己的模块中(即放在它自己的文件中),但这不是必需的。请注意,函数的参数被配置为与函数一起使用:时间 t 是第二个参数。
def vectorfield(w, t, p):
"""
Defines the differential equations for the coupled spring-mass system.
Arguments:
w : vector of the state variables:
w = [x1,y1,x2,y2]
t : time
p : vector of the parameters:
p = [m1,m2,k1,k2,L1,L2,b1,b2]
"""
x1, y1, x2, y2 = w
m1, m2, k1, k2, L1, L2, b1, b2 = p
# Create f = (x1',y1',x2',y2'):
f = [y1,
(-b1 * y1 - k1 * (x1 - L1) + k2 * (x2 - x1 - L2)) / m1,
y2,
(-b2 * y2 - k2 * (x2 - x1 - L2)) / m2]
return f
接下来,这里有一个脚本,用于求解一组给定参数值、初始条件和时间间隔的方程。该脚本将解决方案中的点打印到终端。通常情况下,您会将其输出重定向到一个文件。
# Use ODEINT to solve the differential equations defined by the vector field
from scipy.integrate import odeint
# Parameter values
# Masses:
m1 = 1.0
m2 = 1.5
# Spring constants
k1 = 8.0
k2 = 40.0
# Natural lengths
L1 = 0.5
L2 = 1.0
# Friction coefficients
b1 = 0.8
b2 = 0.5
# Initial conditions
# x1 and x2 are the initial displacements; y1 and y2 are the initial velocities
x1 = 0.5
y1 = 0.0
x2 = 2.25
y2 = 0.0
# ODE solver parameters
abserr = 1.0e-8
relerr = 1.0e-6
stoptime = 10.0
numpoints = 250
# Create the time samples for the output of the ODE solver.
# I use a large number of points, only because I want to make
# a plot of the solution that looks nice.
t = [stoptime * float(i) / (numpoints - 1) for i in range(numpoints)]
# Pack up the parameters and initial conditions:
p = [m1, m2, k1, k2, L1, L2, b1, b2]
w0 = [x1, y1, x2, y2]
# Call the ODE solver.
wsol = odeint(vectorfield, w0, t, args=(p,),
atol=abserr, rtol=relerr)
with open('two_springs.dat', 'w') as f:
# Print & save the solution.
for t1, w1 in zip(t, wsol):
print >> f, t1, w1[0], w1[1], w1[2], w1[3]
下面的脚本使用 Matplotlib 绘制由
# Plot the solution that was generated
from numpy import loadtxt
from pylab import figure, plot, xlabel, grid, hold, legend, title, savefig
from matplotlib.font_manager import FontProperties
t, x1, xy, x2, y2 = loadtxt('two_springs.dat', unpack=True)
figure(1, figsize=(6, 4.5))
xlabel('t')
grid(True)
hold(True)
lw = 1
plot(t, x1, 'b', linewidth=lw)
plot(t, x2, 'g', linewidth=lw)
legend((r'$x_1$', r'$x_2$'), prop=FontProperties(size=16))
title('Mass Displacements for the\nCoupled Spring-Mass System')
savefig('two_springs.png', dpi=100)
附件
库尔特韦格-德弗里斯方程
库尔特韦格-德弗里斯方程
本页显示如何使用线的方法在周期域上求解 Korteweg-de Vries 方程,使用伪谱方法计算空间导数。在这种方法中,首先对数据进行快速傅立叶变换,然后乘以适当的值,并通过逆快速傅立叶变换转换回空间域,从而在频域中计算导数。这种区分方法是通过模块中的差异功能实现的。
我们离散化空间域,并使用在 scipy.fftpack 模块中定义的 diff 函数计算空间导数。在下面的代码中,这个函数被赋予别名 psdiff 以避免与 numpy 函数 diff 混淆。通过只离散空间维度,我们得到了一个常微分方程组,它在函数 kdv(u,t,L) 中实现。功能 kdv_solution(u0,t,L) 使用 scipy.integrate.odeint 对本系统进行求解。
#!python
import numpy as np
from scipy.integrate import odeint
from scipy.fftpack import diff as psdiff
def kdv_exact(x, c):
"""Profile of the exact solution to the KdV for a single soliton on the real line."""
u = 0.5*c*np.cosh(0.5*np.sqrt(c)*x)**(-2)
return u
def kdv(u, t, L):
"""Differential equations for the KdV equation, discretized in x."""
# Compute the x derivatives using the pseudo-spectral method.
ux = psdiff(u, period=L)
uxxx = psdiff(u, period=L, order=3)
# Compute du/dt.
dudt = -6*u*ux - uxxx
return dudt
def kdv_solution(u0, t, L):
"""Use odeint to solve the KdV equation on a periodic domain.
`u0` is initial condition, `t` is the array of time values at which
the solution is to be computed, and `L` is the length of the periodic
domain."""
sol = odeint(kdv, u0, t, args=(L,), mxstep=5000)
return sol
if __name__ == "__main__":
# Set the size of the domain, and create the discretized grid.
L = 50.0
N = 64
dx = L / (N - 1.0)
x = np.linspace(0, (1-1.0/N)*L, N)
# Set the initial conditions.
# Not exact for two solitons on a periodic domain, but close enough...
u0 = kdv_exact(x-0.33*L, 0.75) + kdv_exact(x-0.65*L, 0.4)
# Set the time sample grid.
T = 200
t = np.linspace(0, T, 501)
print "Computing the solution."
sol = kdv_solution(u0, t, L)
print "Plotting."
import matplotlib.pyplot as plt
plt.figure(figsize=(6,5))
plt.imshow(sol[::-1, :], extent=[0,L,0,T])
plt.colorbar()
plt.xlabel('x')
plt.ylabel('t')
plt.axis('normal')
plt.title('Korteweg-de Vries on a Periodic Domain')
plt.show()
Computing the solution.
Plotting.
附件
Matplotlib: lotka volterra 教程
Matplotlib: lotka volterra 教程
此示例描述了如何将 ODs 与 scipy.integrate 模块集成,以及如何使用 matplotlib 模块绘制轨迹、方向场和其他信息。
你可以在这里获得本教程的源代码: tutorial_lokta-voltera_v4.py
。
洛特卡-沃尔泰拉模型的介绍
我们将看看 Lotka-Volterra 模型,也称为捕食者-食饵方程,这是一对一阶非线性微分方程,常用于描述两个物种相互作用的生物系统的动力学,一个是捕食者,另一个是猎物。该模型是由阿尔弗雷德·洛特卡于 1925 年和维托·沃尔泰拉于 1926 年独立提出的,可以用
du/dt = a*u - b*u*v
dv/dt = -c*v + d*b*u*v
with the following notations:
* u: number of preys (for example, rabbits)
* v: number of predators (for example, foxes)
* a, b, c, d are constant parameters defining the behavior of the population:
+ a is the natural growing rate of rabbits, when there's no fox
+ b is the natural dying rate of rabbits, due to predation
+ c is the natural dying rate of fox, when there's no rabbit
+ d is the factor describing how many caught rabbits let create a new fox
我们将使用 X=[u,v]来描述两个种群的状态。
方程的定义:
#!python
from numpy import *
import pylab as p
# Definition of parameters
a = 1.
b = 0.1
c = 1.5
d = 0.75
def dX_dt(X, t=0):
""" Return the growth rate of fox and rabbit populations. """
return array([ a*X[0] - b*X[0]*X[1] ,
-c*X[1] + d*b*X[0]*X[1] ])
人口平衡
使用前!SciPy 为了整合这个系统,我们将仔细研究位置平衡。当增长率等于 0 时,平衡发生。这给出了两个固定点:
#!python
X_f0 = array([ 0\. , 0.])
X_f1 = array([ c/(d*b), a/b])
all(dX_dt(X_f0) == zeros(2) ) and all(dX_dt(X_f1) == zeros(2)) # => True
不动点稳定性
在这两个点附近,系统可以线性化:dX_dt = A_f*X 其中 A 是在对应点评估的雅可比矩阵。我们必须定义雅可比矩阵:
#!python
def d2X_dt2(X, t=0):
""" Return the Jacobian matrix evaluated in X. """
return array([[a -b*X[1], -b*X[0] ],
[b*d*X[1] , -c +b*d*X[0]] ])
所以在代表两个物种灭绝的 X_f0 附近,我们有:
#! python
A_f0 = d2X_dt2(X_f0) # >>> array([[ 1\. , -0\. ],
# [ 0\. , -1.5]])
在 X_f0 附近,兔子数量增加,狐狸数量减少。因此原点是一个鞍点。
在 X_f1 附近,我们有:
#!python
A_f1 = d2X_dt2(X_f1) # >>> array([[ 0\. , -2\. ],
# [ 0.75, 0\. ]])
# whose eigenvalues are +/- sqrt(c*a).j:
lambda1, lambda2 = linalg.eigvals(A_f1) # >>> (1.22474j, -1.22474j)
# They are imaginary numbers. The fox and rabbit populations are periodic as follows from further
# analysis. Their period is given by:
T_f1 = 2*pi/abs(lambda1) # >>> 5.130199
使用 scipy.integrate 集成 ODE
现在我们将使用 scipy.integrate 模块来集成这些操作系统。这个模块提供了一个名为 odeint 的方法,它非常容易用来集成 ODEs:
#!python
from scipy import integrate
t = linspace(0, 15, 1000) # time
X0 = array([10, 5]) # initials conditions: 10 rabbits and 5 foxes
X, infodict = integrate.odeint(dX_dt, X0, t, full_output=True)
infodict['message'] # >>> 'Integration successful.'
infodict
是可选的,如果不想要可以省略full_output
参数。如果您想了解有关 odeint 输入和输出的更多信息,请键入“info(odeint)”。
我们现在可以使用 Matplotlib 来绘制这两个种群的进化:
#!python
rabbits, foxes = X.T
f1 = p.figure()
p.plot(t, rabbits, 'r-', label='Rabbits')
p.plot(t, foxes , 'b-', label='Foxes')
p.grid()
p.legend(loc='best')
p.xlabel('time')
p.ylabel('population')
p.title('Evolution of fox and rabbit populations')
f1.savefig('rabbits_and_foxes_1.png')
总体确实是周期性的,它们的周期接近我们计算的值 T_f1。
在相平面中绘制方向场和轨迹
对于 X_f0 和 X_f1 之间的不同起始点,我们将在相平面中绘制一些轨迹。
我们将使用 Matplotlib 的颜色映射来定义轨迹的颜色。这些彩色地图对制作精美的绘图非常有用。想了解更多信息,可以看看 ShowColormaps 。
values = linspace(0.3, 0.9, 5) # position of X0 between X_f0 and X_f1
vcolors = p.cm.autumn_r(linspace(0.3, 1., len(values))) # colors for each trajectory
f2 = p.figure()
#-------------------------------------------------------
# plot trajectories
for v, col in zip(values, vcolors):
X0 = v * X_f1 # starting point
X = integrate.odeint( dX_dt, X0, t) # we don't need infodict here
p.plot( X[:,0], X[:,1], lw=3.5*v, color=col, label='X0=(%.f, %.f)' % ( X0[0], X0[1]) )
#-------------------------------------------------------
# define a grid and compute direction at each point
ymax = p.ylim(ymin=0)[1] # get axis limits
xmax = p.xlim(xmin=0)[1]
nb_points = 20
x = linspace(0, xmax, nb_points)
y = linspace(0, ymax, nb_points)
X1 , Y1 = meshgrid(x, y) # create a grid
DX1, DY1 = dX_dt([X1, Y1]) # compute growth rate on the gridt
M = (hypot(DX1, DY1)) # Norm of the growth rate
M[ M == 0] = 1\. # Avoid zero division errors
DX1 /= M # Normalize each arrows
DY1 /= M
#-------------------------------------------------------
# Drow direction fields, using matplotlib 's quiver function
# I choose to plot normalized arrows and to use colors to give information on
# the growth speed
p.title('Trajectories and direction fields')
Q = p.quiver(X1, Y1, DX1, DY1, M, pivot='mid', cmap=p.cm.jet)
p.xlabel('Number of rabbits')
p.ylabel('Number of foxes')
p.legend()
p.grid()
p.xlim(0, xmax)
p.ylim(0, ymax)
f2.savefig('rabbits_and_foxes_2.png')
这张图告诉我们,改变狐狸或兔子的数量会产生非直觉的影响。如果为了减少兔子的数量,我们引入狐狸,从长远来看,这可能会导致兔子的增加,这取决于干预的时间。
绘制等高线
我们可以验证下面定义的函数 IF 沿轨迹保持不变:
#!python
def IF(X):
u, v = X
return u**(c/a) * v * exp( -(b/a)*(d*u+v) )
# We will verify that IF remains constant for different trajectories
for v in values:
X0 = v * X_f1 # starting point
X = integrate.odeint( dX_dt, X0, t)
I = IF(X.T) # compute IF along the trajectory
I_mean = I.mean()
delta = 100 * (I.max()-I.min())/I_mean
print 'X0=(%2.f,%2.f) => I ~ %.1f |delta = %.3G %%' % (X0[0], X0[1], I_mean, delta)
# >>> X0=( 6, 3) => I ~ 20.8 |delta = 6.19E-05 %
# X0=( 9, 4) => I ~ 39.4 |delta = 2.67E-05 %
# X0=(12, 6) => I ~ 55.7 |delta = 1.82E-05 %
# X0=(15, 8) => I ~ 66.8 |delta = 1.12E-05 %
# X0=(18, 9) => I ~ 72.4 |delta = 4.68E-06 %
绘制中频的等值线可以很好地表示轨迹,而不必集成常微分方程
#!python
#-------------------------------------------------------
# plot iso contours
nb_points = 80 # grid size
x = linspace(0, xmax, nb_points)
y = linspace(0, ymax, nb_points)
X2 , Y2 = meshgrid(x, y) # create the grid
Z2 = IF([X2, Y2]) # compute IF on each point
f3 = p.figure()
CS = p.contourf(X2, Y2, Z2, cmap=p.cm.Purples_r, alpha=0.5)
CS2 = p.contour(X2, Y2, Z2, colors='black', linewidths=2\. )
p.clabel(CS2, inline=1, fontsize=16, fmt='%.f')
p.grid()
p.xlabel('Number of rabbits')
p.ylabel('Number of foxes')
p.ylim(1, ymax)
p.xlim(1, xmax)
p.title('IF contours')
f3.savefig('rabbits_and_foxes_3.png')
p.show()
附件
rabbits_and_foxes_1.png
rabbits_and_foxes_1v2.png
rabbits_and_foxes_2.png
rabbits_and_foxes_2v2.png
rabbits_and_foxes_2v3.png
rabbits_and_foxes_3.png
rabbits_and_foxes_3v2.png
tutorial_lokta-voltera.py
tutorial_lokta-voltera_v2.py
tutorial_lokta-voltera_v3.py
tutorial_lokta-voltera_v4.py
zombie_nodead_nobirths.png
zombie_somedead_10births.png
zombie_somedead_nobirths.png
zombie_somedeaddead_nobirths.png
模拟僵尸启示录
模拟僵尸启示录
这个例子演示了如何使用 SciPy 求解一阶常微分方程组。注意,一个第 n 阶方程也可以使用 SciPy 通过将其转换为一个一阶方程系统来求解。在这个轻松的例子中,一个常微分方程系统可以用来模拟“僵尸入侵”,使用的是 Munz 等人 2009 中规定的方程。
该系统如下所示:
dS/dt = P-B _ S _ Z-d _ S dZ/dt = B _ S _ Z+G _ R-A _ S _ Z dR/dt = d _ S+A _ S _ Z-G _ R
带有以下符号:
- s:易受影响的受害者人数
- z:僵尸的数量
- r:被“杀死”的人数
- 人口出生率
- 自然死亡的可能性
- “僵尸疾病”传播的几率(一个活着的人变成僵尸)
- g:死人复活成僵尸的几率
- 答:僵尸被彻底消灭的可能性
这包括求解一阶常微分方程组,由下式给出:dy/dt =f(y,t)
其中 y = [S,Z,R]。
用于解决该系统的代码如下:
# zombie apocalypse modeling
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
plt.ion()
plt.rcParams['figure.figsize'] = 10, 8
P = 0 # birth rate
d = 0.0001 # natural death percent (per day)
B = 0.0095 # transmission percent (per day)
G = 0.0001 # resurect percent (per day)
A = 0.0001 # destroy percent (per day)
# solve the system dy/dt = f(y, t)
def f(y, t):
Si = y[0]
Zi = y[1]
Ri = y[2]
# the model equations (see Munz et al. 2009)
f0 = P - B*Si*Zi - d*Si
f1 = B*Si*Zi + G*Ri - A*Si*Zi
f2 = d*Si + A*Si*Zi - G*Ri
return [f0, f1, f2]
# initial conditions
S0 = 500\. # initial population
Z0 = 0 # initial zombie population
R0 = 0 # initial death population
y0 = [S0, Z0, R0] # initial condition vector
t = np.linspace(0, 5., 1000) # time grid
# solve the DEs
soln = odeint(f, y0, t)
S = soln[:, 0]
Z = soln[:, 1]
R = soln[:, 2]
# plot results
plt.figure()
plt.plot(t, S, label='Living')
plt.plot(t, Z, label='Zombies')
plt.xlabel('Days from outbreak')
plt.ylabel('Population')
plt.title('Zombie Apocalypse - No Init. Dead Pop.; No New Births.')
plt.legend(loc=0)
# change the initial conditions
R0 = 0.01*S0 # 1% of initial pop is dead
y0 = [S0, Z0, R0]
# solve the DEs
soln = odeint(f, y0, t)
S = soln[:, 0]
Z = soln[:, 1]
R = soln[:, 2]
plt.figure()
plt.plot(t, S, label='Living')
plt.plot(t, Z, label='Zombies')
plt.xlabel('Days from outbreak')
plt.ylabel('Population')
plt.title('Zombie Apocalypse - 1% Init. Pop. is Dead; No New Births.')
plt.legend(loc=0)
# change the initial conditions
R0 = 0.01*S0 # 1% of initial pop is dead
P = 10 # 10 new births daily
y0 = [S0, Z0, R0]
# solve the DEs
soln = odeint(f, y0, t)
S = soln[:, 0]
Z = soln[:, 1]
R = soln[:, 2]
plt.figure()
plt.plot(t, S, label='Living')
plt.plot(t, Z, label='Zombies')
plt.xlabel('Days from outbreak')
plt.ylabel('Population')
plt.title('Zombie Apocalypse - 1% Init. Pop. is Dead; 10 Daily Births')
plt.legend(loc=0)
<matplotlib.legend.Legend at 0x392ac90>
理论生态学:黑斯廷斯和鲍威尔
理论生态学:黑斯廷斯和鲍威尔
概观
一个简单的脚本,从黑斯廷斯和鲍威尔 1991 年重新创建最小/最大分叉图。
库函数
在模块 bif.py 中定义了两个有用的函数。
import numpy
def window(data, size):
"""A generator that returns the moving window of length
`size` over the `data`
"""
for start in range(len(data) - (size - 1)):
yield data[start:(start + size)]
def min_max(data, tol=1e-14):
"""Return a list of the local min/max found
in a `data` series, given the relative tolerance `tol`
"""
maxes = []
mins = []
for first, second, third in window(data, size=3):
if first < second and third < second:
maxes.append(second)
elif first > second and third > second:
mins.append(second)
elif abs(first - second) < tol and abs(second - third) < tol:
# an equilibrium is both the maximum and minimum
maxes.append(second)
mins.append(second)
return {'max': numpy.asarray(maxes),
'min': numpy.asarray(mins)}
模型
为了提高速度,模型被定义在一个 fortran 文件中,并被编译成一个库,供 python 使用。使用这种方法速度提高了 100 倍。该文件使用 Fortran 90,这使得使用 f2py 变得特别容易。文件名为 hastings.f90。
module model
implicit none
real(8), save :: a1, a2, b1, b2, d1, d2
contains
subroutine fweb(y, t, yprime)
real(8), dimension(3), intent(in) :: y
real(8), intent(in) :: t
real(8), dimension(3), intent(out) :: yprime
yprime(1) = y(1)*(1.0d0 - y(1)) - a1*y(1)*y(2)/(1.0d0 + b1*y(1))
yprime(2) = a1*y(1)*y(2)/(1.0d0 + b1*y(1)) - a2*y(2)*y(3)/(1.0d0 + b2*y(2)) - d1*y(2)
yprime(3) = a2*y(2)*y(3)/(1.0d0 + b2*y(2)) - d2*y(3)
end subroutine fweb
end module model
它是用命令f2py -c -m hastings hastings.f90 --fcompiler=gnu95
编译的(使用 gfortran 编译器)
import numpy
from scipy.integrate import odeint
import bif
import hastings
# setup the food web parameters
hastings.model.a1 = 5.0
hastings.model.a2 = 0.1
hastings.model.b2 = 2.0
hastings.model.d1 = 0.4
hastings.model.d2 = 0.01
# setup the ode solver parameters
t = numpy.arange(10000)
y0 = [0.8, 0.2, 10.0]
def print_max(data, maxfile):
for a_max in data['max']:
print >> maxfile, hastings.model.b1, a_max
x_maxfile = open('x_maxfile.dat', 'w')
y_maxfile = open('y_maxfile.dat', 'w')
z_maxfile = open('z_maxfile.dat', 'w')
for i, hastings.model.b1 in enumerate(numpy.linspace(2.0, 6.2, 420)):
print i, hastings.model.b1
y = odeint(hastings.model.fweb, y0, t)
# use the last 'stationary' solution as an intial guess for the
# next run. This both speeds up the computations, as well as helps
# make sure that solver doesn't need to do too much work.
y0 = y[-1, :]
x_minmax = bif.min_max(y[5000:, 0])
y_minmax = bif.min_max(y[5000:, 1])
z_minmax = bif.min_max(y[5000:, 2])
print_max(x_minmax, x_maxfile)
print_max(y_minmax, y_maxfile)
print_max(z_minmax, z_maxfile)
十七、Numpy 和 Scipy / 其他示例
- 应用 FIR 滤波器
- 布朗运动
- 巴特沃斯带通
- 沟通理论
- 相关随机样本
- 轻松多线程
- 眼图
- FIR 滤波器
- 过滤过滤器
- 寻找二维数据集的凸包
- 求有限点集凸包中的最小点
- 扫频信号
- KDTree 示例
- 卡尔曼滤波
- 线性分类
- 粒子过滤器
- 重新绑定
- 萨维奇基·戈莱滤波
- 1D 信号的平滑
- 2D 信号的平滑
- 求解大型马氏链
- 分水岭
应用 f.i.r 滤波器
应用 f.i.r 滤波器
如何应用 FIR 滤波器:卷积、fftconvolve、卷积 1d 或 lfilter?
下图显示了使用 numpy 和 scipy 中提供的几种不同函数对长度为 131072 的信号应用不同长度的有限脉冲响应(FIR)滤波器所需的时间。下面给出了如何创建该数字的详细信息。
fig
细节
numpy 和 scipy 库中有几个函数可用于将 FIR 滤波器应用于信号。从 scipy.signal 开始,lfilter()被设计为将离散的 IIR 滤波器应用于信号,因此通过简单地将分母系数数组设置为[1.0],它可以用于应用 FIR 滤波器。应用 FIR 滤波器相当于离散的卷积,因此还可以使用 numpy 中的卷积()和 scipy.signal 中的卷积()或 fftconvolve()或 scipy.ndimage 中的卷积 1d()。在本页中,我们演示了这些函数中的每一个,并研究了当数据信号大小固定且 FIR 滤波器长度变化时,计算时间如何变化。我们将使用 131072 的数据信号长度,即 2**17。我们假设我们有 m 个数据通道,所以我们的输入信号是一个由 n 组成的 m 数组。
我们假设我们的 FIR 滤波器系数在一维数组 b 中。numpy.convolve 函数只接受一维数组,所以我们必须在输入数组上使用 python 循环来执行所有通道的卷积。一种方法是
y = np.array([np.convolve(xi, b, mode='valid') for xi in x])
我们使用列表理解来循环遍历 x 的行,并将结果传递给 np.array ,以将过滤后的数据重组为二维数组。
signal .卷积和 signal.fftconvolve 都执行二维数组的二维卷积。为了用这些函数中的任何一个来过滤我们的 m 的 n 数组,我们将我们的过滤器成形为二维数组,用透镜(b) 来成形 1。python 代码如下所示:
y = convolve(x, b[np.newaxis, :], mode='valid')
其中 x 是形状为 (m,n) 的 numpy 数组, b 是 FIR 滤波器系数的一维数组。b【NP . new axis,】是 b 的二维视图,形状 1 由透镜(b) 表示。 y 为过滤后的数据;它只包括计算完整卷积的那些项,因此它具有形状 (m,n - len(b) + 1) 。
ndi image .卷积 1d()被设计为沿着另一个 n 维数组的给定轴对 1d 数组进行卷积。它没有选项 mode='valid ',因此为了提取结果的有效部分,我们对函数的结果进行切片:
y = convolve1d(x, b)[:, (len(b)-1)//2 : -(len(b)//2)]
signal.lfilter 设计用于过滤一维数据。它可以采用一个二维数组(或者,一般来说,一个 n 维数组),并在任何给定的轴上过滤数据。它也可以用于 IIR 滤波器,因此在我们的例子中,我们将传递[1.0]作为分母系数。在 python 中,这看起来像:
y = lfilter(b, [1.0], x)
为了获得与卷积或 fftconvolve 计算的完全相同的数组(即,为了获得“有效”模式的等价形式),我们必须丢弃由 lfilter 计算的数组的开头。我们可以通过在调用 filter 后立即对数组进行切片来实现这一点:
y = lfilter(b, [1.0], x)[:, len(b) - 1:]
以下脚本计算并绘制了将 FIR 滤波器应用于 2 乘 131072 数据数组的结果,其中一系列 FIR 滤波器的长度不断增加。
#!python
import time
import numpy as np
from numpy import convolve as np_convolve
from scipy.signal import convolve as sig_convolve, fftconvolve, lfilter, firwin
from scipy.ndimage import convolve1d
from pylab import grid, show, legend, loglog, xlabel, ylabel, figure
# Create the m by n data to be filtered.
m = 4
n = 2 ** 17
x = np.random.random(size=(m, n))
conv_time = []
npconv_time = []
fftconv_time = []
conv1d_time = []
lfilt_time = []
diff_list = []
diff2_list = []
diff3_list = []
ntaps_list = 2 ** np.arange(2, 13)
for ntaps in ntaps_list:
# Create a FIR filter.
b = firwin(ntaps, [0.05, 0.95], width=0.05, pass_zero=False)
if ntaps <= 2 ** 9:
# --- signal.convolve ---
# We know this is slower than the others when ntaps is
# large, so we only compute it for small values.
tstart = time.time()
conv_result = sig_convolve(x, b[np.newaxis, :], mode='valid')
conv_time.append(time.time() - tstart)
# --- numpy.convolve ---
tstart = time.time()
npconv_result = np.array([np_convolve(xi, b, mode='valid') for xi in x])
npconv_time.append(time.time() - tstart)
# --- signal.fftconvolve ---
tstart = time.time()
fftconv_result = fftconvolve(x, b[np.newaxis, :], mode='valid')
fftconv_time.append(time.time() - tstart)
# --- convolve1d ---
tstart = time.time()
# convolve1d doesn't have a 'valid' mode, so we expliclity slice out
# the valid part of the result.
conv1d_result = convolve1d(x, b)[:, (len(b)-1)//2 : -(len(b)//2)]
conv1d_time.append(time.time() - tstart)
# --- lfilter ---
tstart = time.time()
lfilt_result = lfilter(b, [1.0], x)[:, len(b) - 1:]
lfilt_time.append(time.time() - tstart)
diff = np.abs(fftconv_result - lfilt_result).max()
diff_list.append(diff)
diff2 = np.abs(conv1d_result - lfilt_result).max()
diff2_list.append(diff2)
diff3 = np.abs(npconv_result - lfilt_result).max()
diff3_list.append(diff3)
# Verify that np.convolve and lfilter gave the same results.
print "Did np.convolve and lfilter produce the same results?",
check = all(diff < 1e-13 for diff in diff3_list)
if check:
print "Yes."
else:
print "No! Something went wrong."
# Verify that fftconvolve and lfilter gave the same results.
print "Did fftconvolve and lfilter produce the same results?",
check = all(diff < 1e-13 for diff in diff_list)
if check:
print "Yes."
else:
print "No! Something went wrong."
# Verify that convolve1d and lfilter gave the same results.
print "Did convolve1d and lfilter produce the same results?",
check = all(diff2 < 1e-13 for diff2 in diff2_list)
if check:
print "Yes."
else:
print "No! Something went wrong."
fig = figure(1, figsize=(8, 5.5))
loglog(ntaps_list, npconv_time, 'c-s', label='numpy.convolve')
loglog(ntaps_list, conv1d_time, 'k-p', label='ndimage.convolve1d')
loglog(ntaps_list, fftconv_time, 'g-*', markersize=8, label='signal.fftconvolve')
loglog(ntaps_list[:len(conv_time)], conv_time, 'm-d', label='signal.convolve')
loglog(ntaps_list, lfilt_time, 'b-o', label='signal.lfilter')
legend(loc='best', numpoints=1)
grid(True)
xlabel('Number of taps')
ylabel('Time to filter (seconds)')
Did np.convolve and lfilter produce the same results? Yes.
Did fftconvolve and lfilter produce the same results? Yes.
Did convolve1d and lfilter produce the same results? Yes.
<matplotlib.text.Text at 0x4612c50>
该图显示,根据抽头数,scipy . ndi image .卷积 1d、numpy .卷积或 scipy.signal.fftconvolve 是最快的。上面的脚本可以用来探索这些结果的变体。
布朗运动
布朗运动
布朗运动是一个随机过程。布朗运动方程的一种形式是
\(X(0) = X_0\)
\(X(t + dt) = X(t) + N(0, (delta)^2 dt; t, t+dt)\)
其中\(N(a, b; t_1, t_2)\)
为均值为 a、方差为 b 的正态分布随机变量,参数 t,,1,,和 t,,2,,明确了 N 在不同时间间隔上的统计独立性;也就是说,如果\([t_1, t_2)\)
和\([t_3, t_4)\)
是不相交的区间,那么\(N(a, b; t_1, t_2)\)
和\(N(a, b; t_3, t_4)\)
是独立的。
计算其实很简单。打印布朗运动的 n 步的简单实现可能如下所示:
from scipy.stats import norm
# Process parameters
delta = 0.25
dt = 0.1
# Initial condition.
x = 0.0
# Number of iterations to compute.
n = 20
# Iterate to compute the steps of the Brownian motion.
for k in range(n):
x = x + norm.rvs(scale=delta**2*dt)
print x
0.0149783802189
0.0153383445186
0.0234318982959
0.0212808305024
0.0114258184942
0.01354252966
0.024915691657
0.0225717389674
0.0202001899576
0.0210395736257
0.0346234119557
0.0315723937897
0.0296012566384
0.0296135943506
0.0198499273499
0.0165205162055
0.00688369775327
0.0069949507719
0.00888200058681
0.00297662267152
上面的代码可以很容易地修改,将迭代保存在数组中,而不是打印它们。
上面代码的问题是速度慢。如果我们想计算大量的迭代,我们可以做得更好。关键是要注意,计算是来自正态分布的样本的累积和。快速版本可以这样实现:首先通过调用 scipy.stats.norm.rvs()从正态分布生成所有样本,然后使用 numpy cumsum 函数形成累积和。
下面的函数利用这个思想实现了函数布朗()。该函数允许初始条件是一个数组(或任何可以转换成数组的东西)。 x0 的每个元素都被视为布朗运动的初始条件。
"""
brownian() implements one dimensional Brownian motion (i.e. the Wiener process).
"""
# File: brownian.py
from math import sqrt
from scipy.stats import norm
import numpy as np
def brownian(x0, n, dt, delta, out=None):
"""
Generate an instance of Brownian motion (i.e. the Wiener process):
X(t) = X(0) + N(0, delta**2 * t; 0, t)
where N(a,b; t0, t1) is a normally distributed random variable with mean a and
variance b. The parameters t0 and t1 make explicit the statistical
independence of N on different time intervals; that is, if [t0, t1) and
[t2, t3) are disjoint intervals, then N(a, b; t0, t1) and N(a, b; t2, t3)
are independent.
Written as an iteration scheme,
X(t + dt) = X(t) + N(0, delta**2 * dt; t, t+dt)
If `x0` is an array (or array-like), each value in `x0` is treated as
an initial condition, and the value returned is a numpy array with one
more dimension than `x0`.
Arguments
---------
x0 : float or numpy array (or something that can be converted to a numpy array
using numpy.asarray(x0)).
The initial condition(s) (i.e. position(s)) of the Brownian motion.
n : int
The number of steps to take.
dt : float
The time step.
delta : float
delta determines the "speed" of the Brownian motion. The random variable
of the position at time t, X(t), has a normal distribution whose mean is
the position at time t=0 and whose variance is delta**2*t.
out : numpy array or None
If `out` is not None, it specifies the array in which to put the
result. If `out` is None, a new numpy array is created and returned.
Returns
-------
A numpy array of floats with shape `x0.shape + (n,)`.
Note that the initial value `x0` is not included in the returned array.
"""
x0 = np.asarray(x0)
# For each element of x0, generate a sample of n numbers from a
# normal distribution.
r = norm.rvs(size=x0.shape + (n,), scale=delta*sqrt(dt))
# If `out` was not given, create an output array.
if out is None:
out = np.empty(r.shape)
# This computes the Brownian motion by forming the cumulative sum of
# the random samples.
np.cumsum(r, axis=-1, out=out)
# Add the initial condition.
out += np.expand_dims(x0, axis=-1)
return out
例子
这里有一个脚本,使用这个函数和 matplotlib 的 pylab 模块来绘制布朗运动的几种实现。
import numpy
from pylab import plot, show, grid, xlabel, ylabel
# The Wiener process parameter.
delta = 2
# Total time.
T = 10.0
# Number of steps.
N = 500
# Time step size
dt = T/N
# Number of realizations to generate.
m = 20
# Create an empty array to store the realizations.
x = numpy.empty((m,N+1))
# Initial values of x.
x[:, 0] = 50
brownian(x[:,0], N, dt, delta, out=x[:,1:])
t = numpy.linspace(0.0, N*dt, N+1)
for k in range(m):
plot(t, x[k])
xlabel('t', fontsize=16)
ylabel('x', fontsize=16)
grid(True)
show()
2D 布朗运动
同样的函数可以用来生成二维的布朗运动,因为每个维度都只是一维的布朗运动。
以下脚本提供了一个演示。
import numpy
from pylab import plot, show, grid, axis, xlabel, ylabel, title
# The Wiener process parameter.
delta = 0.25
# Total time.
T = 10.0
# Number of steps.
N = 500
# Time step size
dt = T/N
# Initial values of x.
x = numpy.empty((2,N+1))
x[:, 0] = 0.0
brownian(x[:,0], N, dt, delta, out=x[:,1:])
# Plot the 2D trajectory.
plot(x[0],x[1])
# Mark the start and end points.
plot(x[0,0],x[1,0], 'go')
plot(x[0,-1], x[1,-1], 'ro')
# More plot decorations.
title('2D Brownian Motion')
xlabel('x', fontsize=16)
ylabel('y', fontsize=16)
axis('equal')
grid(True)
show()
巴特沃斯带通
巴特沃斯带通
这本秘籍演示了如何使用scipy.signal.butter
来创建带通巴特沃斯滤波器。scipy.signal.freqz
用于计算频率响应,scipy.signal.lfilter
用于将滤波器应用于信号。(该代码最初是在 stackoverflow.com 回答问题时给出的。)
from scipy.signal import butter, lfilter
def butter_bandpass(lowcut, highcut, fs, order=5):
nyq = 0.5 * fs
low = lowcut / nyq
high = highcut / nyq
b, a = butter(order, [low, high], btype='band')
return b, a
def butter_bandpass_filter(data, lowcut, highcut, fs, order=5):
b, a = butter_bandpass(lowcut, highcut, fs, order=order)
y = lfilter(b, a, data)
return y
def run():
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import freqz
# Sample rate and desired cutoff frequencies (in Hz).
fs = 5000.0
lowcut = 500.0
highcut = 1250.0
# Plot the frequency response for a few different orders.
plt.figure(1)
plt.clf()
for order in [3, 6, 9]:
b, a = butter_bandpass(lowcut, highcut, fs, order=order)
w, h = freqz(b, a, worN=2000)
plt.plot((fs * 0.5 / np.pi) * w, abs(h), label="order = %d" % order)
plt.plot([0, 0.5 * fs], [np.sqrt(0.5), np.sqrt(0.5)],
'--', label='sqrt(0.5)')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Gain')
plt.grid(True)
plt.legend(loc='best')
# Filter a noisy signal.
T = 0.05
nsamples = T * fs
t = np.linspace(0, T, nsamples, endpoint=False)
a = 0.02
f0 = 600.0
x = 0.1 * np.sin(2 * np.pi * 1.2 * np.sqrt(t))
x += 0.01 * np.cos(2 * np.pi * 312 * t + 0.1)
x += a * np.cos(2 * np.pi * f0 * t + .11)
x += 0.03 * np.cos(2 * np.pi * 2000 * t)
plt.figure(2)
plt.clf()
plt.plot(t, x, label='Noisy signal')
y = butter_bandpass_filter(x, lowcut, highcut, fs, order=6)
plt.plot(t, y, label='Filtered signal (%g Hz)' % f0)
plt.xlabel('time (seconds)')
plt.hlines([-a, a], 0, T, linestyles='--')
plt.grid(True)
plt.axis('tight')
plt.legend(loc='upper left')
plt.show()
run()
通信理论
通信理论
这两个例子说明了数字 BPSK 调制通信系统的简单模拟,其中每个符号仅使用一个样本,并且信号仅受 AWGN 噪声的影响。
在第一个例子中,我们循环通过不同的信噪比值,信号长度是理论误差概率的函数。根据经验,我们希望为每个信噪比值计算大约 100 个误差,这决定了信号(和噪声)向量的长度。
#!/usr/bin/python
# BPSK digital modulation example
# by Ivo Maljevic
from numpy import *
from scipy.special import erfc
import matplotlib.pyplot as plt
SNR_MIN = 0
SNR_MAX = 9
Eb_No_dB = arange(SNR_MIN,SNR_MAX+1)
SNR = 10**(Eb_No_dB/10.0) # linear SNR
Pe = empty(shape(SNR))
BER = empty(shape(SNR))
loop = 0
for snr in SNR: # SNR loop
Pe[loop] = 0.5*erfc(sqrt(snr))
VEC_SIZE = ceil(100/Pe[loop]) # vector length is a function of Pe
# signal vector, new vector for each SNR value
s = 2*random.randint(0,high=2,size=VEC_SIZE)-1
# linear power of the noise; average signal power = 1
No = 1.0/snr
# noise
n = sqrt(No/2)*random.randn(VEC_SIZE)
# signal + noise
x = s + n
# decode received signal + noise
y = sign(x)
# find erroneous symbols
err = where(y != s)
error_sum = float(len(err[0]))
BER[loop] = error_sum/VEC_SIZE
print 'Eb_No_dB=%4.2f, BER=%10.4e, Pe=%10.4e' % \
(Eb_No_dB[loop], BER[loop], Pe[loop])
loop += 1
#plt.semilogy(Eb_No_dB, Pe,'r',Eb_No_dB, BER,'s')
plt.semilogy(Eb_No_dB, Pe,'r',linewidth=2)
plt.semilogy(Eb_No_dB, BER,'-s')
plt.grid(True)
plt.legend(('analytical','simulation'))
plt.xlabel('Eb/No (dB)')
plt.ylabel('BER')
plt.show()
Eb_No_dB=0.00, BER=8.0975e-02, Pe=7.8650e-02
Eb_No_dB=1.00, BER=4.9522e-02, Pe=5.6282e-02
Eb_No_dB=2.00, BER=4.3870e-02, Pe=3.7506e-02
Eb_No_dB=3.00, BER=2.0819e-02, Pe=2.2878e-02
Eb_No_dB=4.00, BER=1.1750e-02, Pe=1.2501e-02
Eb_No_dB=5.00, BER=6.2515e-03, Pe=5.9539e-03
Eb_No_dB=6.00, BER=2.2450e-03, Pe=2.3883e-03
Eb_No_dB=7.00, BER=6.3359e-04, Pe=7.7267e-04
Eb_No_dB=8.00, BER=1.8709e-04, Pe=1.9091e-04
Eb_No_dB=9.00, BER=3.0265e-05, Pe=3.3627e-05
在第二个稍加修改的例子中,信号长度增长的问题通过将信号制动成帧来解决。也就是说,给定信噪比下的样本数量增长很快,因此上述模拟不适用于大于 9 或 10 分贝的 Eb/No 值。
#!/usr/bin/python
# BPSK digital modulation: modified example
# by Ivo Maljevic
from scipy import *
from math import sqrt, ceil # scalar calls are faster
from scipy.special import erfc
import matplotlib.pyplot as plt
rand = random.rand
normal = random.normal
SNR_MIN = 0
SNR_MAX = 10
FrameSize = 10000
Eb_No_dB = arange(SNR_MIN,SNR_MAX+1)
Eb_No_lin = 10**(Eb_No_dB/10.0) # linear SNR
# Allocate memory
Pe = empty(shape(Eb_No_lin))
BER = empty(shape(Eb_No_lin))
# signal vector (for faster exec we can repeat the same frame)
s = 2*random.randint(0,high=2,size=FrameSize)-1
loop = 0
for snr in Eb_No_lin:
No = 1.0/snr
Pe[loop] = 0.5*erfc(sqrt(snr))
nFrames = ceil(100.0/FrameSize/Pe[loop])
error_sum = 0
scale = sqrt(No/2)
for frame in arange(nFrames):
# noise
n = normal(scale=scale, size=FrameSize)
# received signal + noise
x = s + n
# detection (information is encoded in signal phase)
y = sign(x)
# error counting
err = where (y != s)
error_sum += len(err[0])
# end of frame loop
##################################################
BER[loop] = error_sum/(FrameSize*nFrames) # SNR loop level
print 'Eb_No_dB=%2d, BER=%10.4e, Pe[loop]=%10.4e' % \
(Eb_No_dB[loop], BER[loop], Pe[loop])
loop += 1
plt.semilogy(Eb_No_dB, Pe,'r',linewidth=2)
plt.semilogy(Eb_No_dB, BER,'-s')
plt.grid(True)
plt.legend(('analytical','simulation'))
plt.xlabel('Eb/No (dB)')
plt.ylabel('BER')
plt.show()
Eb_No_dB= 0, BER=7.6900e-02, Pe[loop]=7.8650e-02
Eb_No_dB= 1, BER=5.5800e-02, Pe[loop]=5.6282e-02
Eb_No_dB= 2, BER=3.7600e-02, Pe[loop]=3.7506e-02
Eb_No_dB= 3, BER=2.2600e-02, Pe[loop]=2.2878e-02
Eb_No_dB= 4, BER=1.2300e-02, Pe[loop]=1.2501e-02
Eb_No_dB= 5, BER=6.6500e-03, Pe[loop]=5.9539e-03
Eb_No_dB= 6, BER=2.3000e-03, Pe[loop]=2.3883e-03
Eb_No_dB= 7, BER=9.0000e-04, Pe[loop]=7.7267e-04
Eb_No_dB= 8, BER=2.0566e-04, Pe[loop]=1.9091e-04
Eb_No_dB= 9, BER=3.3893e-05, Pe[loop]=3.3627e-05
Eb_No_dB=10, BER=4.1425e-06, Pe[loop]=3.8721e-06
附件
相关随机样本
相关随机样本
注: 这个秘籍条目展示了如何使用来自 SciPy 的工具从多元正态分布中生成随机样本,但实际上 NumPy 包含函数numpy.random.multivariate_normal
来完成同样的任务。
为了生成相关的正态分布随机样本,可以首先生成不相关的样本,然后将它们乘以矩阵 C ,使得\(C C^T = R\)
,其中 R 是期望的协方差矩阵。例如,通过使用 R 的乔莱斯基分解,或者从 R 的特征值和特征向量,可以创建 C 。
"""Example of generating correlated normally distributed random samples."""
import numpy as np
from scipy.linalg import eigh, cholesky
from scipy.stats import norm
from pylab import plot, show, axis, subplot, xlabel, ylabel, grid
# Choice of cholesky or eigenvector method.
method = 'cholesky'
#method = 'eigenvectors'
num_samples = 400
# The desired covariance matrix.
r = np.array([
[ 3.40, -2.75, -2.00],
[ -2.75, 5.50, 1.50],
[ -2.00, 1.50, 1.25]
])
# Generate samples from three independent normally distributed random
# variables (with mean 0 and std. dev. 1).
x = norm.rvs(size=(3, num_samples))
# We need a matrix `c` for which `c*c^T = r`. We can use, for example,
# the Cholesky decomposition, or the we can construct `c` from the
# eigenvectors and eigenvalues.
if method == 'cholesky':
# Compute the Cholesky decomposition.
c = cholesky(r, lower=True)
else:
# Compute the eigenvalues and eigenvectors.
evals, evecs = eigh(r)
# Construct c, so c*c^T = r.
c = np.dot(evecs, np.diag(np.sqrt(evals)))
# Convert the data to correlated random variables.
y = np.dot(c, x)
#
# Plot various projections of the samples.
#
subplot(2,2,1)
plot(y[0], y[1], 'b.')
ylabel('y[1]')
axis('equal')
grid(True)
subplot(2,2,3)
plot(y[0], y[2], 'b.')
xlabel('y[0]')
ylabel('y[2]')
axis('equal')
grid(True)
subplot(2,2,4)
plot(y[1], y[2], 'b.')
xlabel('y[1]')
axis('equal')
grid(True)
show()
附件
轻松多线程
轻松多线程
Python 包含一个多线程包“threading”,但是 python 的多线程受到全局解释器 Lock 的严重限制,它一次只允许一个线程与解释器交互。对于纯解释代码,这使得多线程有效协作,无法利用多核。
然而,numpy 代码经常在计算时释放 GIL,因此简单的并行可以加快代码的速度。对于复杂的应用,人们应该直接研究 MPI 或使用线程,但令人惊讶的是,人们的应用通常是“令人尴尬的并行”,也就是说,人们只需对许多对象执行相同的操作,迭代之间没有交互。这种计算很容易并行化:
dft = parallel_map(lambda f: sum(exp(2.j*pi*f*times)), frequencies)
实现 parallel_map 的代码并不太复杂,附在这个条目之后。更简单的是,如果不想返回值:
def compute(n):
...do something...
foreach(compute, range(100))
这将替换 for 循环。
代码见附件(AMArchibald 编写)。[[附件列表]]
另请参见并行编程,了解替代方案和更多讨论。
附件
可见图形
可见图形
下面的代码生成以下图表:
主脚本生成num_traces
轨迹,在 600x600 的网格上,它计算轨迹穿过网格点的次数。然后使用 matplotlib 的 imshow()函数绘制网格。计数使用布雷森汉线算法进行,以确保计数正确,并且曲线的陡峭部分不会导致遗漏计数。
Bresenham 的算法在纯 Python 中速度很慢,所以包含了 Cython 版本。如果您没有构建 Cython 版本的 Bresenham 代码,请确保在运行程序之前减少num_traces
!
下面是主要的演示脚本,eye_demo.py。
#!python
import numpy as np
use_fast = True
try:
from brescount import bres_curve_count
except ImportError:
print "The cython version of the curve counter is not available."
use_fast = False
def bres_segment_count_slow(x0, y0, x1, y1, grid):
"""Bresenham's algorithm.
The value of grid[x,y] is incremented for each x,y
in the line from (x0,y0) up to but not including (x1, y1).
"""
nrows, ncols = grid.shape
dx = abs(x1 - x0)
dy = abs(y1 - y0)
sx = 0
if x0 < x1:
sx = 1
else:
sx = -1
sy = 0
if y0 < y1:
sy = 1
else:
sy = -1
err = dx - dy
while True:
# Note: this test is moved before setting
# the value, so we don't set the last point.
if x0 == x1 and y0 == y1:
break
if 0 <= x0 < nrows and 0 <= y0 < ncols:
grid[x0, y0] += 1
e2 = 2 * err
if e2 > -dy:
err -= dy
x0 += sx
if e2 < dx:
err += dx
y0 += sy
def bres_curve_count_slow(x, y, grid):
for k in range(x.size - 1):
x0 = x[k]
y0 = y[k]
x1 = x[k+1]
y1 = y[k+1]
bres_segment_count_slow(x0, y0, x1, y1, grid)
def random_trace(t):
s = 2*(np.random.randint(0, 5) % 2) - 1
r = 0.01 * np.random.randn()
s += r
a = 2.0 + 0.001 * np.random.randn()
q = 2*(np.random.randint(0, 7) % 2) - 1
t2 = t + q*(6 + 0.01*np.random.randn())
t2 += 0.05*np.random.randn()*t
y = a * (np.exp(s*t2) / (1 + np.exp(s*t2)) - 0.5) + 0.07*np.random.randn()
return y
if __name__ == "__main__":
import matplotlib.pyplot as plt
grid_size = 600
grid = np.zeros((grid_size, grid_size), dtype=np.int32)
tmin = -10.0
tmax = 10.0
n = 81
t = np.linspace(tmin, tmax, n)
dt = (tmax - tmin) / (n - 1)
ymin = -1.5
ymax = 1.5
num_traces = 1000
for k in range(num_traces):
# Add some noise to the times at which the signal
# will be sampled. Without this, all the samples occur
# at the same times, and this produces an aliasing
# effect in the resulting bin counts.
# If n == grid_size, this can be dropped, and t2 = t
# can be used instead. (Or, implement an antialiased
# version of bres_curve_count.)
steps = dt + np.sqrt(0.01 * dt) * np.random.randn(n)
steps[0] = 0
steps_sum = steps.cumsum()
t2 = tmin + (tmax - tmin) * steps_sum / steps_sum[-1]
td = (((t2 - tmin) / (tmax - tmin)) * grid_size).astype(np.int32)
y = random_trace(t2)
# Convert y to integers in the range [0,grid_size).
yd = (((y - ymin) / (ymax - ymin)) * grid_size).astype(np.int32)
if use_fast:
bres_curve_count(td, yd, grid)
else:
bres_curve_count_slow(td, yd, grid)
plt.figure()
# Convert to float32 so we can use nan instead of 0.
grid = grid.astype(np.float32)
grid[grid==0] = np.nan
plt.grid(color='w')
plt.imshow(grid.T[::-1,:], extent=[0,1,0,1], cmap=plt.cm.coolwarm,
interpolation='gaussian')
ax = plt.gca()
ax.set_axis_bgcolor('k')
ax.set_xticks(np.linspace(0,1,11))
ax.set_yticks(np.linspace(0,1,11))
ax.set_xticklabels([])
ax.set_yticklabels([])
plt.colorbar()
fig = plt.gcf()
#plt.savefig("eye-diagram.jpg", bbox_inches='tight')
plt.show()
The cython version of the curve counter is not available.
这是 Bresenham 的线算法的 Cython 实现 brescount.pyx:
#!python
import numpy as np
cimport numpy as np
cimport cython
@cython.boundscheck(False)
cdef int bres_segment_count(unsigned x0, unsigned y0,
unsigned x1, unsigned y1,
np.ndarray[np.int32_t, ndim=2] grid):
"""Bresenham's algorithm.
See http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
"""
cdef unsigned nrows, ncols
cdef int e2, sx, sy, err
cdef int dx, dy
nrows = grid.shape[0]
ncols = grid.shape[1]
if x1 > x0:
dx = x1 - x0
else:
dx = x0 - x1
if y1 > y0:
dy = y1 - y0
else:
dy = y0 - y1
sx = 0
if x0 < x1:
sx = 1
else:
sx = -1
sy = 0
if y0 < y1:
sy = 1
else:
sy = -1
err = dx - dy
while True:
# Note: this test occurs before increment the
# grid value, so we don't count the last point.
if x0 == x1 and y0 == y1:
break
if (x0 < nrows) and (y0 < ncols):
grid[x0, y0] += 1
e2 = 2 * err
if e2 > -dy:
err -= dy
x0 += sx
if e2 < dx:
err += dx
y0 += sy
return 0
def bres_curve_count(np.ndarray[np.int32_t, ndim=1] x,
np.ndarray[np.int32_t, ndim=1] y,
np.ndarray[np.int32_t, ndim=2] grid):
cdef unsigned k
cdef int x0, y0, x1, y1
for k in range(len(x)-1):
x0 = x[k]
y0 = y[k]
x1 = x[k+1]
y1 = y[k+1]
bres_segment_count(x0, y0, x1, y1, grid)
if 0 <= x1 < grid.shape[0] and 0 <= y1 < grid.shape[1]:
grid[x1, y1] += 1
该文件 setup.py 可用于构建 Cython 扩展模块:
#!python
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy
ext = Extension("brescount", ["brescount.pyx"],
include_dirs = [numpy.get_include()])
setup(ext_modules=[ext],
cmdclass = {'build_ext': build_ext})
要构建扩展模块,您必须安装 Cython。
您可以按如下方式构建扩展模块:
$ python setup.py build_ext --inplace
附件
射频滤波器
射频滤波器
本秘籍示例展示了如何使用 scipy.signal 中的函数设计和使用低通 FIR 滤波器。
matplotlib 中的 pylab 模块用于创建地块。
#!python
from numpy import cos, sin, pi, absolute, arange
from scipy.signal import kaiserord, lfilter, firwin, freqz
from pylab import figure, clf, plot, xlabel, ylabel, xlim, ylim, title, grid, axes, show
#------------------------------------------------
# Create a signal for demonstration.
#------------------------------------------------
sample_rate = 100.0
nsamples = 400
t = arange(nsamples) / sample_rate
x = cos(2*pi*0.5*t) + 0.2*sin(2*pi*2.5*t+0.1) + \
0.2*sin(2*pi*15.3*t) + 0.1*sin(2*pi*16.7*t + 0.1) + \
0.1*sin(2*pi*23.45*t+.8)
#------------------------------------------------
# Create a FIR filter and apply it to x.
#------------------------------------------------
# The Nyquist rate of the signal.
nyq_rate = sample_rate / 2.0
# The desired width of the transition from pass to stop,
# relative to the Nyquist rate. We'll design the filter
# with a 5 Hz transition width.
width = 5.0/nyq_rate
# The desired attenuation in the stop band, in dB.
ripple_db = 60.0
# Compute the order and Kaiser parameter for the FIR filter.
N, beta = kaiserord(ripple_db, width)
# The cutoff frequency of the filter.
cutoff_hz = 10.0
# Use firwin with a Kaiser window to create a lowpass FIR filter.
taps = firwin(N, cutoff_hz/nyq_rate, window=('kaiser', beta))
# Use lfilter to filter x with the FIR filter.
filtered_x = lfilter(taps, 1.0, x)
#------------------------------------------------
# Plot the FIR filter coefficients.
#------------------------------------------------
figure(1)
plot(taps, 'bo-', linewidth=2)
title('Filter Coefficients (%d taps)' % N)
grid(True)
#------------------------------------------------
# Plot the magnitude response of the filter.
#------------------------------------------------
figure(2)
clf()
w, h = freqz(taps, worN=8000)
plot((w/pi)*nyq_rate, absolute(h), linewidth=2)
xlabel('Frequency (Hz)')
ylabel('Gain')
title('Frequency Response')
ylim(-0.05, 1.05)
grid(True)
# Upper inset plot.
ax1 = axes([0.42, 0.6, .45, .25])
plot((w/pi)*nyq_rate, absolute(h), linewidth=2)
xlim(0,8.0)
ylim(0.9985, 1.001)
grid(True)
# Lower inset plot
ax2 = axes([0.42, 0.25, .45, .25])
plot((w/pi)*nyq_rate, absolute(h), linewidth=2)
xlim(12.0, 20.0)
ylim(0.0, 0.0025)
grid(True)
#------------------------------------------------
# Plot the original and filtered signals.
#------------------------------------------------
# The phase delay of the filtered signal.
delay = 0.5 * (N-1) / sample_rate
figure(3)
# Plot the original signal.
plot(t, x)
# Plot the filtered signal, shifted to compensate for the phase delay.
plot(t-delay, filtered_x, 'r-')
# Plot just the "good" part of the filtered signal. The first N-1
# samples are "corrupted" by the initial conditions.
plot(t[N-1:]-delay, filtered_x[N-1:], 'g', linewidth=4)
xlabel('t')
grid(True)
show()
最后的图显示了原始信号(细蓝线)、滤波信号(偏移适当的相位延迟以与原始信号对齐;细红线)和滤波信号的“好”部分(粗绿线)。“好的部分”是信号中不受初始条件影响的部分。
附件
Filtfilt
Filtfilt
此示例代码演示了函数 scipy.signal.filtfilt 的使用,这是一种线性滤波器,通过对信号应用两次 IIR 滤波器来实现零相位延迟,一次向前,一次向后。过滤器的顺序是原始过滤器顺序的两倍。该函数还计算初始滤波器参数,以提供更稳定的响应(通过 lfilter_zi)。
为了进行比较,该脚本还使用 scipy.signal.lfilter 对信号应用相同的 IIR 滤波器;对于这些计算,lfilter_zi 用于为滤波器选择合适的初始条件。如果没有这一点,这些图将有接近 0 的长瞬变。事实上,它们在信号初始值附近有很长的瞬变。
密码
from numpy import sin, cos, pi, linspace
from numpy.random import randn
from scipy.signal import lfilter, lfilter_zi, filtfilt, butter
from matplotlib.pyplot import plot, legend, show, hold, grid, figure, savefig
# Generate a noisy signal to be filtered.
t = linspace(-1, 1, 201)
x = (sin(2 * pi * 0.75 * t*(1-t) + 2.1) + 0.1*sin(2 * pi * 1.25 * t + 1) +
0.18*cos(2 * pi * 3.85 * t))
xn = x + randn(len(t)) * 0.08
# Create an order 3 lowpass butterworth filter.
b, a = butter(3, 0.05)
# Apply the filter to xn. Use lfilter_zi to choose the initial condition
# of the filter.
zi = lfilter_zi(b, a)
z, _ = lfilter(b, a, xn, zi=zi*xn[0])
# Apply the filter again, to have a result filtered at an order
# the same as filtfilt.
z2, _ = lfilter(b, a, z, zi=zi*z[0])
# Use filtfilt to apply the filter.
y = filtfilt(b, a, xn)
# Make the plot.
figure(figsize=(10,5))
hold(True)
plot(t, xn, 'b', linewidth=1.75, alpha=0.75)
plot(t, z, 'r--', linewidth=1.75)
plot(t, z2, 'r', linewidth=1.75)
plot(t, y, 'k', linewidth=1.75)
legend(('noisy signal',
'lfilter, once',
'lfilter, twice',
'filtfilt'),
loc='best')
hold(False)
grid(True)
show()
#savefig('plot.png', dpi=65)
寻找二维数据集的凸包
寻找二维数据集的凸包
注意:你可能想用 scipy.spatial .凸包来代替这个。
这段代码找到描述一组二维数据点周围凸包的点的子集。该代码可选地使用 pylab 来制作其进度动画。
import numpy as n, pylab as p, time
def _angle_to_point(point, centre):
'''calculate angle in 2-D between points and x axis'''
delta = point - centre
res = n.arctan(delta[1] / delta[0])
if delta[0] < 0:
res += n.pi
return res
def _draw_triangle(p1, p2, p3, **kwargs):
tmp = n.vstack((p1,p2,p3))
x,y = [x[0] for x in zip(tmp.transpose())]
p.fill(x,y, **kwargs)
#time.sleep(0.2)
def area_of_triangle(p1, p2, p3):
'''calculate area of any triangle given co-ordinates of the corners'''
return n.linalg.norm(n.cross((p2 - p1), (p3 - p1)))/2.
def convex_hull(points, graphic=True, smidgen=0.0075):
'''Calculate subset of points that make a convex hull around points
Recursively eliminates points that lie inside two neighbouring points until only convex hull is remaining.
:Parameters:
points : ndarray (2 x m)
array of points for which to find hull
graphic : bool
use pylab to show progress?
smidgen : float
offset for graphic number labels - useful values depend on your data range
:Returns:
hull_points : ndarray (2 x n)
convex hull surrounding points
'''
if graphic:
p.clf()
p.plot(points[0], points[1], 'ro')
n_pts = points.shape[1]
assert(n_pts > 5)
centre = points.mean(1)
if graphic: p.plot((centre[0],),(centre[1],),'bo')
angles = n.apply_along_axis(_angle_to_point, 0, points, centre)
pts_ord = points[:,angles.argsort()]
if graphic:
for i in xrange(n_pts):
p.text(pts_ord[0,i] + smidgen, pts_ord[1,i] + smidgen, \
'%d' % i)
pts = [x[0] for x in zip(pts_ord.transpose())]
prev_pts = len(pts) + 1
k = 0
while prev_pts > n_pts:
prev_pts = n_pts
n_pts = len(pts)
if graphic: p.gca().patches = []
i = -2
while i < (n_pts - 2):
Aij = area_of_triangle(centre, pts[i], pts[(i + 1) % n_pts])
Ajk = area_of_triangle(centre, pts[(i + 1) % n_pts], \
pts[(i + 2) % n_pts])
Aik = area_of_triangle(centre, pts[i], pts[(i + 2) % n_pts])
if graphic:
_draw_triangle(centre, pts[i], pts[(i + 1) % n_pts], \
facecolor='blue', alpha = 0.2)
_draw_triangle(centre, pts[(i + 1) % n_pts], \
pts[(i + 2) % n_pts], \
facecolor='green', alpha = 0.2)
_draw_triangle(centre, pts[i], pts[(i + 2) % n_pts], \
facecolor='red', alpha = 0.2)
if Aij + Ajk < Aik:
if graphic: p.plot((pts[i + 1][0],),(pts[i + 1][1],),'go')
del pts[i+1]
i += 1
n_pts = len(pts)
k += 1
return n.asarray(pts)
if __name__ == "__main__":
points = n.random.random_sample((2,40))
hull_pts = convex_hull(points)
求有限点集凸包中的最小点
求有限点集凸包中的最小点
基于 Philip Wolf [1]的工作和 Kazuyuki Sekitani 和 yoshitsuku Yamamoto[2]的递归算法。
[2]中的算法有 3ε,以避免算法三个部分的比较问题。下面的代码几乎没有变化,只有一个ε。改变的目的是避免无限循环。
密码
from numpy import array, matrix, sin, sqrt, dot, cos, ix_, zeros, concatenate, abs, log10, exp, ones
from numpy.linalg import norm
from mpmath import mpf, mp
mp.dps=80
def find_min_point(P):
# print "Calling find_min with P: ", P
if len(P) == 1:
return P[0]
eps = mpf(10)**-40
P = [array([mpf(i) for i in p]) for p in P]
# Step 0\. Choose a point from C(P)
x = P[array([dot(p,p) for p in P]).argmin()]
while True:
# Step 1\. \alpha_k := min{x_{k-1}^T p | p \in P}
p_alpha = P[array([dot(x,p) for p in P]).argmin()]
if dot(x,x-p_alpha) < eps:
return array([float(i) for i in x])
Pk = [p for p in P if abs(dot(x,p-p_alpha)) < eps]
# Step 2\. P_k := { p | p \in P and x_{k-1}^T p = \alpha_k}
P_Pk = [p for p in P if not array([(p == q).all() for q in Pk]).any()]
if len(Pk) == len(P):
return array([float(i) for i in x])
y = find_min_point(Pk)
p_beta = P_Pk[array([dot(y,p) for p in P_Pk]).argmin()]
if dot(y,y-p_beta) < eps:
return array([float(i) for i in y])
# Step 4.
P_aux = [p for p in P_Pk if (dot(y-x,y-p)>eps) and (dot(x,y-p)!=0)]
p_lambda = P_aux[array([dot(y,y-p)/dot(x,y-p) for p in P_aux]).argmin()]
lam = dot(x,p_lambda-y) / dot(y-x,y-p_lambda)
x += lam * (y-x)
if __name__ == '__main__':
print find_min_point( [array([ -4.83907292e+00, 2.22438863e+04, -2.67496763e+04]), array([ 9.71147604, -351.46404195, -292.18064276]), array([ 4.60452808e+00, 1.07020174e+05, -1.25310230e+05]), array([ 2.16080134e+00, 5.12019937e+04, -5.96167833e+04]), array([ 2.65472146e+00, 6.70546443e+04, -7.71619656e+04]), array([ 1.55775358e+00, -1.34347516e+05, 1.53209265e+05]), array([ 13.22464295, 1869.01251292, -2137.61850989])])
print find_min_point( [array([ -4.83907292e+00, 2.22438863e+04, -2.67496763e+04]), array([ 9.71147604, -351.46404195, -292.18064276]), array([ 4.60452808e+00, 1.07020174e+05, -1.25310230e+05]), array([ 2.16080134e+00, 5.12019937e+04, -5.96167833e+04]), array([ 2.65472146e+00, 6.70546443e+04, -7.71619656e+04]), array([ 1.55775358e+00, -1.34347516e+05, 1.53209265e+05]), array([ 13.22464295, 1869.01251292, -2137.61850989]), array([ 12273.18670123, -1233.32015854, 61690.10864825])])
[ 13.0643029 -3.03446491 -2.65980139]
[ 1.61870596e-04 -3.78774039e-05 -3.29329552e-05]
参考
扫频信号
扫频信号
本页演示了 scipy.signal 中产生扫频信号的两个功能:chirp
和sweep_poly
。
其中一些需要 SciPy 0.8。
要运行代码示例,您需要以下导入:
import numpy as np
from scipy.signal import chirp, sweep_poly
线性啁啾
示例代码:
t = np.linspace(0, 10, 5001)
w = chirp(t, f0=12.5, f1=2.5, t1=10, method='linear')
二次啁啾
示例代码:
t = np.linspace(0, 10, 5001)
w = chirp(t, f0=12.5, f1=2.5, t1=10, method='quadratic')
使用vertex_zero
的示例代码:
t = np.linspace(0, 10, 5001)
w = chirp(t, f0=12.5, f1=2.5, t1=10, method='quadratic', vertex_zero=False)
对数啁啾
示例代码:
t = np.linspace(0, 10, 5001)
w = chirp(t, f0=12.5, f1=2.5, t1=10, method='logarithmic')
双曲线啁啾
示例代码:
t = np.linspace(0, 10, 5001)
w = chirp(t, f0=12.5, f1=2.5, t1=10, method='hyperbolic')
扫聚
示例代码:
p = np.poly1d([0.05, -0.75, 2.5, 5.0])
t = np.linspace(0, 10, 5001)
w = sweep_poly(t, p)
生成剧情的脚本是 here
附件
chirp_hyperbolic.png
chirp_linear.png
chirp_logarithmic.png
chirp_plot.py
chirp_quadratic.png
chirp_quadratic_v0false.png
sweep_poly.png
KDTree 示例
KDTree 示例
'注意:scipy 中有一个 kdtree 的实现:http://docs . scipy . org/scipy/docs/scipy . spatial . kdtree . kdtree/建议用那个代替下面的。'
这是一个如何用 NumPy 在 Python 中构建和搜索 kd 树的例子。kd 树例如用于在多维空间中搜索相邻的数据点。在 KD-树中搜索所有 n 个点的最近邻居相对于样本大小具有 O(n log n)复杂度。
构建 kd 树
#!python numbers=disable
# Copyleft 2008 Sturla Molden
# University of Oslo
#import psyco
#psyco.full()
import numpy
def kdtree( data, leafsize=10 ):
"""
build a kd-tree for O(n log n) nearest neighbour search
input:
data: 2D ndarray, shape =(ndim,ndata), preferentially C order
leafsize: max. number of data points to leave in a leaf
output:
kd-tree: list of tuples
"""
ndim = data.shape[0]
ndata = data.shape[1]
# find bounding hyper-rectangle
hrect = numpy.zeros((2,data.shape[0]))
hrect[0,:] = data.min(axis=1)
hrect[1,:] = data.max(axis=1)
# create root of kd-tree
idx = numpy.argsort(data[0,:], kind='mergesort')
data[:,:] = data[:,idx]
splitval = data[0,ndata/2]
left_hrect = hrect.copy()
right_hrect = hrect.copy()
left_hrect[1, 0] = splitval
right_hrect[0, 0] = splitval
tree = [(None, None, left_hrect, right_hrect, None, None)]
stack = [(data[:,:ndata/2], idx[:ndata/2], 1, 0, True),
(data[:,ndata/2:], idx[ndata/2:], 1, 0, False)]
# recursively split data in halves using hyper-rectangles:
while stack:
# pop data off stack
data, didx, depth, parent, leftbranch = stack.pop()
ndata = data.shape[1]
nodeptr = len(tree)
# update parent node
_didx, _data, _left_hrect, _right_hrect, left, right = tree[parent]
tree[parent] = (_didx, _data, _left_hrect, _right_hrect, nodeptr, right) if leftbranch \
else (_didx, _data, _left_hrect, _right_hrect, left, nodeptr)
# insert node in kd-tree
# leaf node?
if ndata <= leafsize:
_didx = didx.copy()
_data = data.copy()
leaf = (_didx, _data, None, None, 0, 0)
tree.append(leaf)
# not a leaf, split the data in two
else:
splitdim = depth % ndim
idx = numpy.argsort(data[splitdim,:], kind='mergesort')
data[:,:] = data[:,idx]
didx = didx[idx]
nodeptr = len(tree)
stack.append((data[:,:ndata/2], didx[:ndata/2], depth+1, nodeptr, True))
stack.append((data[:,ndata/2:], didx[ndata/2:], depth+1, nodeptr, False))
splitval = data[splitdim,ndata/2]
if leftbranch:
left_hrect = _left_hrect.copy()
right_hrect = _left_hrect.copy()
else:
left_hrect = _right_hrect.copy()
right_hrect = _right_hrect.copy()
left_hrect[1, splitdim] = splitval
right_hrect[0, splitdim] = splitval
# append node to tree
tree.append((None, None, left_hrect, right_hrect, None, None))
return tree
搜索 kd 树
#!python numbers=disable
def intersect(hrect, r2, centroid):
"""
checks if the hyperrectangle hrect intersects with the
hypersphere defined by centroid and r2
"""
maxval = hrect[1,:]
minval = hrect[0,:]
p = centroid.copy()
idx = p < minval
p[idx] = minval[idx]
idx = p > maxval
p[idx] = maxval[idx]
return ((p-centroid)**2).sum() < r2
def quadratic_knn_search(data, lidx, ldata, K):
""" find K nearest neighbours of data among ldata """
ndata = ldata.shape[1]
param = ldata.shape[0]
K = K if K < ndata else ndata
retval = []
sqd = ((ldata - data[:,:ndata])**2).sum(axis=0) # data.reshape((param,1)).repeat(ndata, axis=1);
idx = numpy.argsort(sqd, kind='mergesort')
idx = idx[:K]
return zip(sqd[idx], lidx[idx])
def search_kdtree(tree, datapoint, K):
""" find the k nearest neighbours of datapoint in a kdtree """
stack = [tree[0]]
knn = [(numpy.inf, None)]*K
_datapt = datapoint[:,0]
while stack:
leaf_idx, leaf_data, left_hrect, \
right_hrect, left, right = stack.pop()
# leaf
if leaf_idx is not None:
_knn = quadratic_knn_search(datapoint, leaf_idx, leaf_data, K)
if _knn[0][0] < knn[-1][0]:
knn = sorted(knn + _knn)[:K]
# not a leaf
else:
# check left branch
if intersect(left_hrect, knn[-1][0], _datapt):
stack.append(tree[left])
# chech right branch
if intersect(right_hrect, knn[-1][0], _datapt):
stack.append(tree[right])
return knn
def knn_search( data, K, leafsize=2048 ):
""" find the K nearest neighbours for data points in data,
using an O(n log n) kd-tree """
ndata = data.shape[1]
param = data.shape[0]
# build kdtree
tree = kdtree(data.copy(), leafsize=leafsize)
# search kdtree
knn = []
for i in numpy.arange(ndata):
_data = data[:,i].reshape((param,1)).repeat(leafsize, axis=1);
_knn = search_kdtree(tree, _data, K+1)
knn.append(_knn[1:])
return knn
def radius_search(tree, datapoint, radius):
""" find all points within radius of datapoint """
stack = [tree[0]]
inside = []
while stack:
leaf_idx, leaf_data, left_hrect, \
right_hrect, left, right = stack.pop()
# leaf
if leaf_idx is not None:
param=leaf_data.shape[0]
distance = numpy.sqrt(((leaf_data - datapoint.reshape((param,1)))**2).sum(axis=0))
near = numpy.where(distance<=radius)
if len(near[0]):
idx = leaf_idx[near]
distance = distance[near]
inside += (zip(distance, idx))
else:
if intersect(left_hrect, radius, datapoint):
stack.append(tree[left])
if intersect(right_hrect, radius, datapoint):
stack.append(tree[right])
return inside
小数据集的二次搜索
与 kd 树相反,直接穷举搜索相对于样本大小具有二次复杂度。当样本量很小时,它可能比使用 kd-tree 更快。在我的电脑上,大约有 500 个或更少的样本。
#!python numbers=disable
def knn_search( data, K ):
""" find the K nearest neighbours for data points in data,
using O(n**2) search """
ndata = data.shape[1]
knn = []
idx = numpy.arange(ndata)
for i in numpy.arange(ndata):
_knn = quadratic_knn_search(data[:,i], idx, data, K+1) # see above
knn.append( _knn[1:] )
return knn
大型数据集的并行搜索
虽然创建一个 kd-tree 非常快,但是搜索它可能很耗时。由于 Python 可怕的“全局解释器锁”(GIL),线程不能用于并行执行多个搜索。也就是说,Python 线程可以用于异步,但不能用于并发。但是,我们可以使用多个进程(多个解释器)。 pyprocessing 包使这变得容易。它有一个类似于 Python 的线程和队列标准模块的 API,但是使用进程而不是线程。从 Python 2.6 开始,pyprocessing 已经作为“多处理”模块包含在 Python 的标准库中。使用多个进程会有少量开销,包括进程创建、进程启动、进程间通信和进程终止。但是,由于进程在不同的地址空间中运行,因此不会发生内存争用。在下面的示例中,与计算相比,使用多个进程的开销非常小,这使得速度接近计算机上的 CPU 数量。
#!python numbers=disable
try:
import multiprocessing as processing
except:
import processing
import ctypes, os
def __num_processors():
if os.name == 'nt': # Windows
return int(os.getenv('NUMBER_OF_PROCESSORS'))
else: # glibc (Linux, *BSD, Apple)
get_nprocs = ctypes.cdll.libc.get_nprocs
get_nprocs.restype = ctypes.c_int
get_nprocs.argtypes = []
return get_nprocs()
def __search_kdtree(tree, data, K, leafsize):
knn = []
param = data.shape[0]
ndata = data.shape[1]
for i in numpy.arange(ndata):
_data = data[:,i].reshape((param,1)).repeat(leafsize, axis=1);
_knn = search_kdtree(tree, _data, K+1)
knn.append(_knn[1:])
return knn
def __remote_process(rank, qin, qout, tree, K, leafsize):
while 1:
# read input queue (block until data arrives)
nc, data = qin.get()
# process data
knn = __search_kdtree(tree, data, K, leafsize)
# write to output queue
qout.put((nc,knn))
def knn_search_parallel(data, K, leafsize=2048):
""" find the K nearest neighbours for data points in data,
using an O(n log n) kd-tree, exploiting all logical
processors on the computer """
ndata = data.shape[1]
param = data.shape[0]
nproc = __num_processors()
# build kdtree
tree = kdtree(data.copy(), leafsize=leafsize)
# compute chunk size
chunk_size = data.shape[1] / (4*nproc)
chunk_size = 100 if chunk_size < 100 else chunk_size
# set up a pool of processes
qin = processing.Queue(maxsize=ndata/chunk_size)
qout = processing.Queue(maxsize=ndata/chunk_size)
pool = [processing.Process(target=__remote_process,
args=(rank, qin, qout, tree, K, leafsize))
for rank in range(nproc)]
for p in pool: p.start()
# put data chunks in input queue
cur, nc = 0, 0
while 1:
_data = data[:,cur:cur+chunk_size]
if _data.shape[1] == 0: break
qin.put((nc,_data))
cur += chunk_size
nc += 1
# read output queue
knn = []
while len(knn) < nc:
knn += [qout.get()]
# avoid race condition
_knn = [n for i,n in sorted(knn)]
knn = []
for tmp in _knn:
knn += tmp
# terminate workers
for p in pool: p.terminate()
return knn
运行代码
下面显示了如何运行示例代码(包括如何格式化输入数据):
#!python numbers=disable
from time import clock
def test():
K = 11
ndata = 10000
ndim = 12
data = 10 * numpy.random.rand(ndata*ndim).reshape((ndim,ndata) )
knn_search(data, K)
if __name__ == '__main__':
t0 = clock()
test()
t1 = clock()
print "Elapsed time %.2f seconds" % t1-t0
#import profile # using Python's profiler is not useful if you are
#profile.run('test()') # running the parallel search.
卡尔曼滤波
卡尔曼滤波
这是实现卡尔曼滤波器简介第 11-15 页给出的例子的代码,作者是格雷格·韦尔奇和加里·毕肖普,北卡罗来纳大学教堂山分校计算机科学系。
# Kalman filter example demo in Python
# A Python implementation of the example given in pages 11-15 of "An
# Introduction to the Kalman Filter" by Greg Welch and Gary Bishop,
# University of North Carolina at Chapel Hill, Department of Computer
# Science, TR 95-041,
# http://www.cs.unc.edu/~welch/kalman/kalmanIntro.html
# by Andrew D. Straw
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = (10, 8)
# intial parameters
n_iter = 50
sz = (n_iter,) # size of array
x = -0.37727 # truth value (typo in example at top of p. 13 calls this z)
z = np.random.normal(x,0.1,size=sz) # observations (normal about x, sigma=0.1)
Q = 1e-5 # process variance
# allocate space for arrays
xhat=np.zeros(sz) # a posteri estimate of x
P=np.zeros(sz) # a posteri error estimate
xhatminus=np.zeros(sz) # a priori estimate of x
Pminus=np.zeros(sz) # a priori error estimate
K=np.zeros(sz) # gain or blending factor
R = 0.1**2 # estimate of measurement variance, change to see effect
# intial guesses
xhat[0] = 0.0
P[0] = 1.0
for k in range(1,n_iter):
# time update
xhatminus[k] = xhat[k-1]
Pminus[k] = P[k-1]+Q
# measurement update
K[k] = Pminus[k]/( Pminus[k]+R )
xhat[k] = xhatminus[k]+K[k]*(z[k]-xhatminus[k])
P[k] = (1-K[k])*Pminus[k]
plt.figure()
plt.plot(z,'k+',label='noisy measurements')
plt.plot(xhat,'b-',label='a posteri estimate')
plt.axhline(x,color='g',label='truth value')
plt.legend()
plt.title('Estimate vs. iteration step', fontweight='bold')
plt.xlabel('Iteration')
plt.ylabel('Voltage')
plt.figure()
valid_iter = range(1,n_iter) # Pminus not valid at step 0
plt.plot(valid_iter,Pminus[valid_iter],label='a priori error estimate')
plt.title('Estimated $\it{\mathbf{a \ priori}}$ error vs. iteration step', fontweight='bold')
plt.xlabel('Iteration')
plt.ylabel('$(Voltage)^2$')
plt.setp(plt.gca(),'ylim',[0,.01])
plt.show()
线性分类
线性分类
费希尔线性判别式
第一个例子展示了费希尔线性分类器在 2 类问题中的实现,克里斯托弗·M·毕肖普在《模式识别和机器学习》一书中对该算法进行了精确描述(第 186 页,第 4.1 节)。该算法的主要思想是,我们尝试降低输入向量 X 的维数,并使用等式 y = W . T X 将其投影到 1D 空间,其中 W.T -行是权重向量,我们调整权重向量 W 并选择最大化类分离的投影。下面的程序使用了具有 150 个实例和 4 个属性的 famouse 数据集 Iris(4D 空间),目标向量包含标签:“Iris-setosa”、“Iris-virginica”、“Iris-versicolor”,因此,我们有 3 个类,但是,在这种情况下,我们可以假设我们有带有标签“Iris-setosa”的类 1 和带有其他实例的类 2。Iris 数据集可在此处获得:http://archive.ics.uci.edu/ml/datasets/Iris/或此处(逗号分隔格式)-bezdekIris.data.txt
T4】
#! python
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
def read_data():
f=open("Iris.txt", 'r')
lines=[line.strip() for line in f.readlines()]
f.close()
lines=[line.split(",") for line in lines if line]
class1=np.array([line[:4] for line in lines if line[-1]=="Iris-setosa"], dtype=np.float)
class2=np.array([line[:4] for line in lines if line[-1]!="Iris-setosa"], dtype=np.float)
return class1, class2
def main():
class1, class2=read_data()
mean1=np.mean(class1, axis=0)
mean2=np.mean(class2, axis=0)
#calculate variance within class
Sw=np.dot((class1-mean1).T, (class1-mean1))+np.dot((class2-mean2).T, (class2-mean2))
#calculate weights which maximize linear separation
w=np.dot(np.linalg.inv(Sw), (mean2-mean1))
print "vector of max weights", w
#projection of classes on 1D space
plt.plot(np.dot(class1, w), [0]*class1.shape[0], "bo", label="Iris-setosa")
plt.plot(np.dot(class2, w), [0]*class2.shape[0], "go", label="Iris-versicolor and Iris-virginica")
plt.legend()
plt.show()
main()
概率生成模型
这个程序是 K 类问题概率生成模型的实现,克里斯托弗·M·毕肖普在《模式识别和机器学习》一书中也有描述(第 196 页,第 4.2 节)。我们尝试学习每一类 K 的类条件密度(似然)p(x|Ck)和先验概率密度 p(Ck),然后利用贝叶斯规则计算后验概率 p(Ck|x)。这里我们假设 p(x|Ck)是 4D 高斯,参数是 K 类的 uk -均值向量,K 类的 Sk -协方差矩阵,并且所有 K 的 p(Ck)是 1/3。然后我们计算所谓的量 ak(程序中的变量 pc)和如果 ak>>aj 为所有 k!=j 然后分配 p(Ck|x)=1 和 p(Cj|x)=0。
#! python
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
import math
def read_data():
f=open("Iris.txt", 'r')
lines=[line.strip() for line in f.readlines()]
f.close()
lines=[line.split(",") for line in lines if line]
data=np.array([line[:4] for line in lines if line], dtype=np.float)
class1=np.array([line[:4] for line in lines if line[-1]=="Iris-setosa"], dtype=np.float)
class2=np.array([line[:4] for line in lines if line[-1]=="Iris-virginica"], dtype=np.float)
class3=np.array([line[:4] for line in lines if line[-1]=="Iris-versicolor"], dtype=np.float)
#list of class labels
labels=[]
for line in lines:
strt=line.pop()
labels.append(strt)
#create array of labels
labels=[line.split(",") for line in labels if line]
t=np.zeros(shape=(150, 3))
#create target vector encoded according to 1-of-K scheme
for i in xrange(len(data)):
if labels[i]==["Iris-setosa"]: t[i][0]=1
elif labels[i]==["Iris-versicolor"]: t[i][1]=1
elif labels[i]==["Iris-virginica"]: t[i][2]=1
return class1, class2, class3, data, t
def gaussian(x, mean, cov):
xm=np.reshape((x-mean), (-1, 1))
px=1/(math.pow(2.0*math.pi, 2))*1/math.sqrt(np.linalg.det(cov))*math.exp(-(np.dot(np.dot(xm.T, np.linalg.inv(cov)), xm))/2)
return px
def main():
class1, class2, class3, data, t=read_data()
count=np.zeros(shape=(150,1))
t_assigned=np.zeros(shape=(150, 3))
cov=np.zeros(shape=(3, 4, 4))
mean=np.zeros(shape=(3, 4))
#compute means for each class
mean1=class1.mean(axis=0)
mean2=class2.mean(axis=0)
mean3=class3.mean(axis=0)
#compute covariance matrices, such that the columns are variables and rows are observations of variables
cov1=np.cov(class1, rowvar=0)
cov2=np.cov(class2, rowvar=0)
cov3=np.cov(class3, rowvar=0)
#compute gaussian likelihood functions p(x|Ck) for each class
for i in xrange(len(data)):
px1=(1/3.0)*gaussian(data[i], mean1, cov1)
px2=(1/3.0)*gaussian(data[i], mean2, cov2)
px3=(1/3.0)*gaussian(data[i], mean3, cov3)
m=np.max([px1, px2, px3])
#compute posterior probability p(Ck|x) assuming that p(x|Ck) is gaussian and the entire expression is wrapped by sigmoid function
pc1=((math.exp(px1)*math.exp(-m))*math.exp(m))/((math.exp(px2)*math.exp(-m)+math.exp(px3)*math.exp(-m))*math.exp(m))
pc2=((math.exp(px2)*math.exp(-m))*math.exp(m))/((math.exp(px1)*math.exp(-m)+math.exp(px3)*math.exp(-m))*math.exp(m))
pc3=((math.exp(px3)*math.exp(-m))*math.exp(m))/((math.exp(px1)*math.exp(-m)+math.exp(px2)*math.exp(-m))*math.exp(m))
#assign p(Ck|x)=1 if p(Ck|x)>>p(Cj|x) for all j!=k
if pc1>pc2 and pc1>pc3: t_assigned[i][0]=1
elif pc3>pc1 and pc3>pc2: t_assigned[i][1]=1
elif pc2>pc1 and pc2>pc3: t_assigned[i][2]=1
#count the number of misclassifications
for j in xrange(3):
if t[i][j]-t_assigned[i][j]!=0: count[i]=1
cov=[cov1, cov2, cov3]
mean=[mean1, mean2, mean3]
t1=np.zeros(shape=(len(class1), 1))
t2=np.zeros(shape=(len(class2), 1))
t3=np.zeros(shape=(len(class3), 1))
for i in xrange(len(data)):
for j in xrange(len(class1)):
if t_assigned[i][0]==1: t1[j]=1
elif t_assigned[i][1]==1: t2[j]=2
elif t_assigned[i][2]==1: t3[j]=3
plt.plot(t1, "bo", label="Iris-setosa")
plt.plot(t2, "go", label="Iris-versicolor")
plt.plot(t3, "ro", label="Iris-virginica")
plt.legend()
plt.show()
print "number of misclassifications", sum(count), "assigned labels to data points", t_assigned, "target data", t
main()
该程序导致所有 150 个实例中的错误分类数= 3
附件
Fisher_discriminant.png
Fisher_disrciminant.JPG
Fisher_disrciminant.PNG
Probabilistic_model.PNG
bezdekIris.data.txt
fisher
prob_gen_model.png
粒子过滤器
粒子过滤器
一种基本的粒子滤波跟踪算法,使用均匀分布的步长作为运动模型,初始目标颜色作为加权函数的决定特征。这需要一个颜色近似均匀的物体,其移动速度不大于每帧步长。
这个实现假设视频流是一个 numpy 数组序列,一个指向这个序列的迭代器或者一个生成这个序列的生成器。粒子滤波器本身是一个生成器,允许对实时视频流进行操作。
#!python
from numpy import *
from numpy.random import *
def resample(weights):
n = len(weights)
indices = []
C = [0.] + [sum(weights[:i+1]) for i in range(n)]
u0, j = random(), 0
for u in [(u0+i)/n for i in range(n)]:
while u > C[j]:
j+=1
indices.append(j-1)
return indices
def particlefilter(sequence, pos, stepsize, n):
seq = iter(sequence)
x = ones((n, 2), int) * pos # Initial position
f0 = seq.next()[tuple(pos)] * ones(n) # Target colour model
yield pos, x, ones(n)/n # Return expected position, particles and weights
for im in seq:
x += uniform(-stepsize, stepsize, x.shape) # Particle motion model: uniform step
x = x.clip(zeros(2), array(im.shape)-1).astype(int) # Clip out-of-bounds particles
f = im[tuple(x.T)] # Measure particle colours
w = 1./(1\. + (f0-f)**2) # Weight~ inverse quadratic colour distance
w /= sum(w) # Normalize w
yield sum(x.T*w, axis=1), x, w # Return expected position, particles and weights
if 1./sum(w**2) < n/2.: # If particle cloud degenerate:
x = x[resample(w),:] # Resample particles according to weights
下面的代码显示了跟踪器在一个测试序列上运行,该序列的特征是一个在统一背景下移动的正方形。
#!python
if __name__ == "__main__":
from pylab import *
from itertools import izip
import time
ion()
seq = [ im for im in zeros((20,240,320), int)] # Create an image sequence of 20 frames long
x0 = array([120, 160]) # Add a square with starting position x0 moving along trajectory xs
xs = vstack((arange(20)*3, arange(20)*2)).T + x0
for t, x in enumerate(xs):
xslice = slice(x[0]-8, x[0]+8)
yslice = slice(x[1]-8, x[1]+8)
seq[t][xslice, yslice] = 255
for im, p in izip(seq, particlefilter(seq, x0, 8, 100)): # Track the square through the sequence
pos, xs, ws = p
position_overlay = zeros_like(im)
position_overlay[tuple(pos)] = 1
particle_overlay = zeros_like(im)
particle_overlay[tuple(xs.T)] = 1
hold(True)
draw()
time.sleep(0.3)
clf() # Causes flickering, but without the spy plots aren't overwritten
imshow(im,cmap=cm.gray) # Plot the image
spy(position_overlay, marker='.', color='b') # Plot the expected position
spy(particle_overlay, marker=',', color='r') # Plot the particles
show()
附件
重新绑定
重新绑定
显示如何重新绑定数据以生成更小或更大的数组,而不使用(和使用)插值的示例。
例 1
这里,我们处理最简单的情况,其中任何所需的新形状都是有效的,并且没有对数据进行插值来确定新值。首先,为每个维度创建浮动切片对象。其次,使用 mgrid 根据切片计算新面元的坐标。然后,坐标被转换成整数指数。最后,“花式索引”用于在所需索引处评估原始数组。
def rebin( a, newshape ):
'''Rebin an array to a new shape.
'''
assert len(a.shape) == len(newshape)
slices = [ slice(0,old, float(old)/new) for old,new in zip(a.shape,newshape) ]
coordinates = mgrid[slices]
indices = coordinates.astype('i') #choose the biggest smaller integer index
return a[tuple(indices)]
如果我们只对将尺寸缩小整数倍感兴趣,那么我们可以使用:
def rebin_factor( a, newshape ):
'''Rebin an array to a new shape.
newshape must be a factor of a.shape.
'''
assert len(a.shape) == len(newshape)
assert not sometrue(mod( a.shape, newshape ))
slices = [ slice(None,None, old/new) for old,new in zip(a.shape,newshape) ]
return a[slices]
例 2
这里有另一种方法来处理 ndarrays 的缩减情况。这与 IDL 的 rebin 命令作用相同,在该命令中,原始数组中的所有值被求和,并在新数组中的条目之间进行划分。正如在 IDL 中一样,新的形状必须是旧形状的一个因素。丑陋的“evList 技巧”构建并执行一个 python 命令
a .重塑(参数[0],因子[0])。sum(1)/factor[0] a .重塑(args[0],factor[0],args[1],factor[1,)。总和(1)。总和(2)/因子[0]/因子[1]
等等。这种通用形式被扩展到包括所需尺寸的数量。
def rebin(a, *args):
'''rebin ndarray data into a smaller ndarray of the same rank whose dimensions
are factors of the original dimensions. eg. An array with 6 columns and 4 rows
can be reduced to have 6,3,2 or 1 columns and 4,2 or 1 rows.
example usages:
>>> a=rand(6,4); b=rebin(a,3,2)
>>> a=rand(6); b=rebin(a,2)
'''
shape = a.shape
lenShape = len(shape)
factor = asarray(shape)/asarray(args)
evList = ['a.reshape('] + \
['args[%d],factor[%d],'%(i,i) for i in range(lenShape)] + \
[')'] + ['.sum(%d)'%(i+1) for i in range(lenShape)] + \
['/factor[%d]'%i for i in range(lenShape)]
print ''.join(evList)
return eval(''.join(evList))
上面的代码返回一个与输入数组类型相同的数组。如果输入是整数数组,输出值将向下舍入。如果您想要一个无需舍入就能正确平均输入值的浮点数组,您可以执行以下操作。
a .重塑(参数[0],因子[0])。均值(1) a .重塑(args[0],因子[0],args[1],因子[1])。平均值(1)。平均值(2)
def rebin(a, *args):
shape = a.shape
lenShape = len(shape)
factor = asarray(shape)/asarray(args)
evList = ['a.reshape('] + \
['args[%d],factor[%d],'%(i,i) for i in range(lenShape)] + \
[')'] + ['.mean(%d)'%(i+1) for i in range(lenShape)]
print ''.join(evList)
return eval(''.join(evList))
一些测试案例:
# 2-D case
a=rand(6,4)
print a
b=rebin(a,6,4)
print b
b=rebin(a,6,2)
print b
b=rebin(a,3,2)
print b
b=rebin(a,1,1)
# 1-D case
print b
a=rand(4)
print a
b=rebin(a,4)
print b
b=rebin(a,2)
print b
b=rebin(a,1)
print b
例 3
在 IDL 中使用的 congrid 的 python 版本,使用各种最近邻和插值例程将数据重新采样到任意大小。
import numpy as n
import scipy.interpolate
import scipy.ndimage
def congrid(a, newdims, method='linear', centre=False, minusone=False):
'''Arbitrary resampling of source array to new dimension sizes.
Currently only supports maintaining the same number of dimensions.
To use 1-D arrays, first promote them to shape (x,1).
Uses the same parameters and creates the same co-ordinate lookup points
as IDL''s congrid routine, which apparently originally came from a VAX/VMS
routine of the same name.
method:
neighbour - closest value from original data
nearest and linear - uses n x 1-D interpolations using
scipy.interpolate.interp1d
(see Numerical Recipes for validity of use of n 1-D interpolations)
spline - uses ndimage.map_coordinates
centre:
True - interpolation points are at the centres of the bins
False - points are at the front edge of the bin
minusone:
For example- inarray.shape = (i,j) & new dimensions = (x,y)
False - inarray is resampled by factors of (i/x) * (j/y)
True - inarray is resampled by(i-1)/(x-1) * (j-1)/(y-1)
This prevents extrapolation one element beyond bounds of input array.
'''
if not a.dtype in [n.float64, n.float32]:
a = n.cast[float](a)
m1 = n.cast[int](minusone)
ofs = n.cast[int](centre) * 0.5
old = n.array( a.shape )
ndims = len( a.shape )
if len( newdims ) != ndims:
print "[congrid] dimensions error. " \
"This routine currently only support " \
"rebinning to the same number of dimensions."
return None
newdims = n.asarray( newdims, dtype=float )
dimlist = []
if method == 'neighbour':
for i in range( ndims ):
base = n.indices(newdims)[i]
dimlist.append( (old[i] - m1) / (newdims[i] - m1) \
* (base + ofs) - ofs )
cd = n.array( dimlist ).round().astype(int)
newa = a[list( cd )]
return newa
elif method in ['nearest','linear']:
# calculate new dims
for i in range( ndims ):
base = n.arange( newdims[i] )
dimlist.append( (old[i] - m1) / (newdims[i] - m1) \
* (base + ofs) - ofs )
# specify old dims
olddims = [n.arange(i, dtype = n.float) for i in list( a.shape )]
# first interpolation - for ndims = any
mint = scipy.interpolate.interp1d( olddims[-1], a, kind=method )
newa = mint( dimlist[-1] )
trorder = [ndims - 1] + range( ndims - 1 )
for i in range( ndims - 2, -1, -1 ):
newa = newa.transpose( trorder )
mint = scipy.interpolate.interp1d( olddims[i], newa, kind=method )
newa = mint( dimlist[i] )
if ndims > 1:
# need one more transpose to return to original dimensions
newa = newa.transpose( trorder )
return newa
elif method in ['spline']:
oslices = [ slice(0,j) for j in old ]
oldcoords = n.ogrid[oslices]
nslices = [ slice(0,j) for j in list(newdims) ]
newcoords = n.mgrid[nslices]
newcoords_dims = range(n.rank(newcoords))
#make first index last
newcoords_dims.append(newcoords_dims.pop(0))
newcoords_tr = newcoords.transpose(newcoords_dims)
# makes a view that affects newcoords
newcoords_tr += ofs
deltas = (n.asarray(old) - m1) / (newdims - m1)
newcoords_tr *= deltas
newcoords_tr -= ofs
newa = scipy.ndimage.map_coordinates(a, newcoords)
return newa
else:
print "Congrid error: Unrecognized interpolation type.\n", \
"Currently only \'neighbour\', \'nearest\',\'linear\',", \
"and \'spline\' are supported."
return None
萨维奇基·戈雷滤波
萨维奇基·戈雷滤波
Savitzky Golay 滤波器是一种特殊类型的低通滤波器,非常适合数据平滑。有关更多信息,请参见:http://www.wire.tu-bs.de/OLDWEB/mameyer/cmr/savgol.pdf(或http://www . dalkescientific . com/writes/NBN/data/savitzky _ golay . py了解 numpy 之前的实现)。
示例代码
#!python
def savitzky_golay(y, window_size, order, deriv=0, rate=1):
r"""Smooth (and optionally differentiate) data with a Savitzky-Golay filter.
The Savitzky-Golay filter removes high frequency noise from data.
It has the advantage of preserving the original shape and
features of the signal better than other types of filtering
approaches, such as moving averages techniques.
Parameters
----------
y : array_like, shape (N,)
the values of the time history of the signal.
window_size : int
the length of the window. Must be an odd integer number.
order : int
the order of the polynomial used in the filtering.
Must be less then `window_size` - 1.
deriv: int
the order of the derivative to compute (default = 0 means only smoothing)
Returns
-------
ys : ndarray, shape (N)
the smoothed signal (or it's n-th derivative).
Notes
-----
The Savitzky-Golay is a type of low-pass filter, particularly
suited for smoothing noisy data. The main idea behind this
approach is to make for each point a least-square fit with a
polynomial of high order over a odd-sized window centered at
the point.
Examples
--------
t = np.linspace(-4, 4, 500)
y = np.exp( -t**2 ) + np.random.normal(0, 0.05, t.shape)
ysg = savitzky_golay(y, window_size=31, order=4)
import matplotlib.pyplot as plt
plt.plot(t, y, label='Noisy signal')
plt.plot(t, np.exp(-t**2), 'k', lw=1.5, label='Original signal')
plt.plot(t, ysg, 'r', label='Filtered signal')
plt.legend()
plt.show()
References
----------
.. [1] A. Savitzky, M. J. E. Golay, Smoothing and Differentiation of
Data by Simplified Least Squares Procedures. Analytical
Chemistry, 1964, 36 (8), pp 1627-1639.
.. [2] Numerical Recipes 3rd Edition: The Art of Scientific Computing
W.H. Press, S.A. Teukolsky, W.T. Vetterling, B.P. Flannery
Cambridge University Press ISBN-13: 9780521880688
"""
import numpy as np
from math import factorial
try:
window_size = np.abs(np.int(window_size))
order = np.abs(np.int(order))
except ValueError, msg:
raise ValueError("window_size and order have to be of type int")
if window_size % 2 != 1 or window_size < 1:
raise TypeError("window_size size must be a positive odd number")
if window_size < order + 2:
raise TypeError("window_size is too small for the polynomials order")
order_range = range(order+1)
half_window = (window_size -1) // 2
# precompute coefficients
b = np.mat([[k**i for i in order_range] for k in range(-half_window, half_window+1)])
m = np.linalg.pinv(b).A[deriv] * rate**deriv * factorial(deriv)
# pad the signal at the extremes with
# values taken from the signal itself
firstvals = y[0] - np.abs( y[1:half_window+1][::-1] - y[0] )
lastvals = y[-1] + np.abs(y[-half_window-1:-1][::-1] - y[-1])
y = np.concatenate((firstvals, y, lastvals))
return np.convolve( m[::-1], y, mode='valid')
代码解释
在线 61-62 中,预先计算局部最小二乘多项式拟合的系数。这些将在稍后的线路 68 上使用,在那里它们将与信号相关联。为了防止在数据的极端出现虚假结果,信号的两端都填充了镜像(第 65-67 行)。
数字
蛋白质的镉光谱。黑色:原始数据。红色:应用了过滤器
循环伏安法数据包装器
除了平滑紫外-可见和红外光谱之外,S-G 滤光器最受欢迎的应用之一是平滑在电分析实验中获得的曲线。在循环伏安法中,电压(abcissa)像三角波一样变化。并且在信号中,在转折点(在开关电势处)有永远不应该平滑的尖点。在这种情况下,Savitzky-Golay 平滑应该分段进行,即。在 x 方向上单调的片段上分别进行:
#!python numbers=disable
def savitzky_golay_piecewise(xvals, data, kernel=11, order =4):
turnpoint=0
last=len(xvals)
if xvals[1]>xvals[0] : #x is increasing?
for i in range(1,last) : #yes
if xvals[i]<xvals[i-1] : #search where x starts to fall
turnpoint=i
break
else: #no, x is decreasing
for i in range(1,last) : #search where it starts to rise
if xvals[i]>xvals[i-1] :
turnpoint=i
break
if turnpoint==0 : #no change in direction of x
return savitzky_golay(data, kernel, order)
else:
#smooth the first piece
firstpart=savitzky_golay(data[0:turnpoint],kernel,order)
#recursively smooth the rest
rest=savitzky_golay_piecewise(xvals[turnpoint:], data[turnpoint:], kernel, order)
return numpy.concatenate((firstpart,rest))
二维数据平滑和最小二乘梯度估计
Savitsky-Golay 滤波器也可用于平滑受噪声影响的二维数据。算法和一维情况完全一样,只是数学稍微复杂一点。基本算法如下:1 .对于二维矩阵的每个点,提取一个子矩阵,以该点为中心,其大小等于奇数“window_size”。2.对于这个子矩阵,计算多项式曲面最小二乘拟合,定义为 p(x,y)= A0+a1x+a2y+a3x2+a4*y2+a5x*y+...。注意 x 和 y 在中心点等于零。3.用拟合计算的值替换初始中心点。
请注意,因为拟合系数相对于数据间距是线性的,所以可以预先计算它们以提高效率。此外,用数据本身的镜像适当填充数据的边界是很重要的,这样数据边界处的拟合评估可以顺利进行。
这是二维过滤的代码。
#!python numbers=enable
def sgolay2d ( z, window_size, order, derivative=None):
"""
"""
# number of terms in the polynomial expression
n_terms = ( order + 1 ) * ( order + 2) / 2.0
if window_size % 2 == 0:
raise ValueError('window_size must be odd')
if window_size**2 < n_terms:
raise ValueError('order is too high for the window size')
half_size = window_size // 2
# exponents of the polynomial.
# p(x,y) = a0 + a1*x + a2*y + a3*x^2 + a4*y^2 + a5*x*y + ...
# this line gives a list of two item tuple. Each tuple contains
# the exponents of the k-th term. First element of tuple is for x
# second element for y.
# Ex. exps = [(0,0), (1,0), (0,1), (2,0), (1,1), (0,2), ...]
exps = [ (k-n, n) for k in range(order+1) for n in range(k+1) ]
# coordinates of points
ind = np.arange(-half_size, half_size+1, dtype=np.float64)
dx = np.repeat( ind, window_size )
dy = np.tile( ind, [window_size, 1]).reshape(window_size**2, )
# build matrix of system of equation
A = np.empty( (window_size**2, len(exps)) )
for i, exp in enumerate( exps ):
A[:,i] = (dx**exp[0]) * (dy**exp[1])
# pad input array with appropriate values at the four borders
new_shape = z.shape[0] + 2*half_size, z.shape[1] + 2*half_size
Z = np.zeros( (new_shape) )
# top band
band = z[0, :]
Z[:half_size, half_size:-half_size] = band - np.abs( np.flipud( z[1:half_size+1, :] ) - band )
# bottom band
band = z[-1, :]
Z[-half_size:, half_size:-half_size] = band + np.abs( np.flipud( z[-half_size-1:-1, :] ) -band )
# left band
band = np.tile( z[:,0].reshape(-1,1), [1,half_size])
Z[half_size:-half_size, :half_size] = band - np.abs( np.fliplr( z[:, 1:half_size+1] ) - band )
# right band
band = np.tile( z[:,-1].reshape(-1,1), [1,half_size] )
Z[half_size:-half_size, -half_size:] = band + np.abs( np.fliplr( z[:, -half_size-1:-1] ) - band )
# central band
Z[half_size:-half_size, half_size:-half_size] = z
# top left corner
band = z[0,0]
Z[:half_size,:half_size] = band - np.abs( np.flipud(np.fliplr(z[1:half_size+1,1:half_size+1]) ) - band )
# bottom right corner
band = z[-1,-1]
Z[-half_size:,-half_size:] = band + np.abs( np.flipud(np.fliplr(z[-half_size-1:-1,-half_size-1:-1]) ) - band )
# top right corner
band = Z[half_size,-half_size:]
Z[:half_size,-half_size:] = band - np.abs( np.flipud(Z[half_size+1:2*half_size+1,-half_size:]) - band )
# bottom left corner
band = Z[-half_size:,half_size].reshape(-1,1)
Z[-half_size:,:half_size] = band - np.abs( np.fliplr(Z[-half_size:, half_size+1:2*half_size+1]) - band )
# solve system and convolve
if derivative == None:
m = np.linalg.pinv(A)[0].reshape((window_size, -1))
return scipy.signal.fftconvolve(Z, m, mode='valid')
elif derivative == 'col':
c = np.linalg.pinv(A)[1].reshape((window_size, -1))
return scipy.signal.fftconvolve(Z, -c, mode='valid')
elif derivative == 'row':
r = np.linalg.pinv(A)[2].reshape((window_size, -1))
return scipy.signal.fftconvolve(Z, -r, mode='valid')
elif derivative == 'both':
c = np.linalg.pinv(A)[1].reshape((window_size, -1))
r = np.linalg.pinv(A)[2].reshape((window_size, -1))
return scipy.signal.fftconvolve(Z, -r, mode='valid'), scipy.signal.fftconvolve(Z, -c, mode='valid')
这是一个演示
#!python number=enable
# create some sample twoD data
x = np.linspace(-3,3,100)
y = np.linspace(-3,3,100)
X, Y = np.meshgrid(x,y)
Z = np.exp( -(X**2+Y**2))
# add noise
Zn = Z + np.random.normal( 0, 0.2, Z.shape )
# filter it
Zf = sgolay2d( Zn, window_size=29, order=4)
# do some plotting
matshow(Z)
matshow(Zn)
matshow(Zf)
附件:Original.pdf 原始数据附件:Original+noise.pdf 原始数据+noise 附件:Original+noise+filter . pdf(原始数据+noise)filter
二维函数的梯度
因为我们已经计算了最佳拟合插值多项式曲面,所以很容易计算它的梯度。这种计算二维函数梯度的方法相当稳健,并且部分地隐藏了数据中的噪声,这强烈地影响了微分操作。可以计算的导数的最大阶数显然取决于拟合中使用的多项式的阶数。
上面提供的代码有一个选项derivative
,它现在允许计算 2D 数据的一阶导数。它可以是“行”或“列”,指示导数的方向,也可以是“两者”,返回梯度。
附件
1D 信号的平滑
1D 信号的平滑
该方法基于缩放窗口与信号的卷积。通过在两端引入信号的反射窗口长度副本来准备信号,以便在输出信号的开始和结束部分最小化边界效应。
密码
import numpy
def smooth(x,window_len=11,window='hanning'):
"""smooth the data using a window with requested size.
This method is based on the convolution of a scaled window with the signal.
The signal is prepared by introducing reflected copies of the signal
(with the window size) in both ends so that transient parts are minimized
in the begining and end part of the output signal.
input:
x: the input signal
window_len: the dimension of the smoothing window; should be an odd integer
window: the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'
flat window will produce a moving average smoothing.
output:
the smoothed signal
example:
t=linspace(-2,2,0.1)
x=sin(t)+randn(len(t))*0.1
y=smooth(x)
see also:
numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve
scipy.signal.lfilter
TODO: the window parameter could be the window itself if an array instead of a string
NOTE: length(output) != length(input), to correct this: return y[(window_len/2-1):-(window_len/2)] instead of just y.
"""
if x.ndim != 1:
raise ValueError, "smooth only accepts 1 dimension arrays."
if x.size < window_len:
raise ValueError, "Input vector needs to be bigger than window size."
if window_len<3:
return x
if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']:
raise ValueError, "Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'"
s=numpy.r_[x[window_len-1:0:-1],x,x[-1:-window_len:-1]]
#print(len(s))
if window == 'flat': #moving average
w=numpy.ones(window_len,'d')
else:
w=eval('numpy.'+window+'(window_len)')
y=numpy.convolve(w/w.sum(),s,mode='valid')
return y
from numpy import *
from pylab import *
def smooth_demo():
t=linspace(-4,4,100)
x=sin(t)
xn=x+randn(len(t))*0.1
y=smooth(x)
ws=31
subplot(211)
plot(ones(ws))
windows=['flat', 'hanning', 'hamming', 'bartlett', 'blackman']
hold(True)
for w in windows[1:]:
eval('plot('+w+'(ws) )')
axis([0,30,0,1.1])
legend(windows)
title("The smoothing windows")
subplot(212)
plot(x)
plot(xn)
for w in windows:
plot(smooth(xn,10,w))
l=['original signal', 'signal with noise']
l.extend(windows)
legend(l)
title("Smoothing a noisy signal")
show()
if __name__=='__main__':
smooth_demo()
数字
[]文件/附件/信号平滑/平滑信号. jpg
求解大型马氏链
求解大型马氏链
本页展示了如何计算大型马尔可夫链的平稳分布 pi。这个例子是两个 M/M/1 队列的串联。通常马氏链的转移矩阵 P 是稀疏的,因此我们可以使用 scipy.sparse 或 Pysparse。这里我们演示如何使用这两种工具。
幂法
在这一节中,我们通过称为幂法的迭代方法来求圆周率。更具体地说,给定一个(随机)转移矩阵 P 和一个初始向量 pi 0,迭代计算 pi_n = pi {n-1} P,直到 pi n 和 pi {n-1}之间的距离(在某个范数下)足够小。
首先,我们为相关的马尔可夫链建立生成矩阵 Q。然后我们用均匀化的方法把 q 变成一个转移矩阵 p,也就是我们把 p 定义为 I - Q/l,其中 I 是单位矩阵(与 q 大小相同),l 是 q 对角线上的最小元素,一旦我们有了 p,我们就用迭代的 pi_n = pi_0 P^n 来近似 pi(满足 pi = pi P 的 p 的左特征向量),对于一些初始向量 pi_0。
以上方法的更多细节可以在(或多或少)任何一本关于概率和马尔可夫链的书中找到。一个很好的例子,有许多很好的例子,并关注马氏链的数值解,是“排队网络和马氏链”由 g .博尔奇等人,约翰威利,第二版,2006 年。
你可以在这里获得本教程的源代码: tandemqueue.py
#!/usr/bin/env python
labda, mu1, mu2 = 1., 1.01, 1.001
N1, N2 = 50, 50
size = N1*N2
from numpy import ones, zeros, empty
import scipy.sparse as sp
import pysparse
from pylab import matshow, savefig
from scipy.linalg import norm
import time
这个简单的函数将表示第一个队列包含 I 个作业,第二个队列包含 j 个作业的状态(I,j)转换成更合适的形式来定义转移矩阵。
def state(i,j):
return j*N1 + i
构建生成器矩阵 q 的非对角元素
def fillOffDiagonal(Q):
# labda
for i in range(0,N1-1):
for j in range(0,N2):
Q[(state(i,j),state(i+1,j))]= labda
# mu2
for i in range(0,N1):
for j in range(1,N2):
Q[(state(i,j),state(i,j-1))]= mu2
# mu1
for i in range(1,N1):
for j in range(0,N2-1):
Q[(state(i,j),state(i-1,j+1))]= mu1
print "ready filling"
在这个函数中,我们使用 scipy.sparse
def computePiMethod1():
e0 = time.time()
Q = sp.dok_matrix((size,size))
fillOffDiagonal(Q)
# Set the diagonal of Q such that the row sums are zero
Q.setdiag( -Q*ones(size) )
# Compute a suitable stochastic matrix by means of uniformization
l = min(Q.values())*1.001 # avoid periodicity, see the book of Bolch et al.
P = sp.speye(size, size) - Q/l
# compute Pi
P = P.tocsr()
pi = zeros(size); pi1 = zeros(size)
pi[0] = 1;
n = norm(pi - pi1,1); i = 0;
while n > 1e-3 and i < 1e5:
pi1 = pi*P
pi = pi1*P # avoid copying pi1 to pi
n = norm(pi - pi1,1); i += 1
print "Method 1: ", time.time() - e0, i
return pi
现在使用 Pysparse。
def computePiMethod2():
e0 = time.time()
Q = pysparse.spmatrix.ll_mat(size,size)
fillOffDiagonal(Q)
# fill diagonal
x = empty(size)
Q.matvec(ones(size),x)
Q.put(-x)
# uniformize
l = min(Q.values())*1.001
P = pysparse.spmatrix.ll_mat(size,size)
P.put(ones(size))
P.shift(-1./l, Q)
# Compute pi
P = P.to_csr()
pi = zeros(size); pi1 = zeros(size)
pi[0] = 1;
n = norm(pi - pi1,1); i = 0;
while n > 1e-3 and i < 1e5:
P.matvec_transp(pi,pi1)
P.matvec_transp(pi1,pi)
n = norm(pi - pi1,1); i += 1
print "Method 2: ", time.time() - e0, i
return pi
输出结果。
def plotPi(pi):
pi = pi.reshape(N2,N1)
matshow(pi)
savefig("pi.eps")
if __name__ == "__main__":
pi = computePiMethod1()
pi = computePiMethod2()
plotPi(pi)
结果如下:
本教程的改进
包括其他方法,如雅可比方法或高斯塞德尔。
附件
流域
流域
分水岭算法(参见 1用于将图像分割成不同的部分。
假设我们有下面的图像,由三个白色圆盘(像素值为 1)和一个黑色背景(像素值为 0)组成。我们希望获得一个新的数组,其中每个像素都用其所属组件的索引进行标记,这是原始数组的分割,如下图所示。我们将使用 scipy.ndimage、scipy.ndimage .分水岭 _ift 提供的分水岭算法。
# Create the initial black and white image
import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt
a = np.zeros((512, 512)).astype(np.uint8) #unsigned integer type needed by watershed
y, x = np.ogrid[0:512, 0:512]
m1 = ((y-200)**2 + (x-100)**2 < 30**2)
m2 = ((y-350)**2 + (x-400)**2 < 20**2)
m3 = ((y-260)**2 + (x-200)**2 < 20**2)
a[m1+m2+m3]=1
plt.imshow(a, cmap='gray')# left plot in the image above
<matplotlib.image.AxesImage at 0x7ff9c8348690>
分水岭算法依赖于不同流域的淹没,因此我们需要在图像中放置标记来启动淹没。如果人们大致知道物体在哪里,并且只有几个物体,就有可能用手设置标记
markers = np.zeros_like(a).astype(np.int16)
markers[0, 0] = 1
markers[200, 100] = 2
markers[350, 400] = 3
markers[260, 200] = 4
res1 = ndimage.watershed_ift(a.astype(np.uint8), markers)
plt.imshow(res1, cmap='jet') # central plot in the image above
<matplotlib.image.AxesImage at 0x7ff9c827a550>
np.unique(res1) # pixels are tagged according to the object they belong to
array([1, 2, 3, 4], dtype=int16)
如果您不知道标记放在哪里,并且知道对象的最小尺寸,您可以在比对象更细的网格上散布许多标记。
xm, ym = np.ogrid[0:512:10, 0:512:10]
markers = np.zeros_like(a).astype(np.int16)
markers[xm, ym]= np.arange(xm.size*ym.size).reshape((xm.size,ym.size))
res2 = ndimage.watershed_ift(a.astype(np.uint8), markers)
res2[xm, ym] = res2[xm-1, ym-1] # remove the isolate seeds
plt.imshow(res2, cmap='jet')
<matplotlib.image.AxesImage at 0x7ff9c81a3a10>
十八、NumPy 和 SciPy / 求根
函数交集
函数交集
找出两个给定函数相交的点
考虑寻找多项式和直线的交点的例子:
\(y_1=x_1^2\)
\(y_2=x_2+1\)
from scipy.optimize import fsolve
import numpy as np
def f(xy):
x, y = xy
z = np.array([y - x**2, y - x - 1.0])
return z
fsolve(f, [1.0, 2.0])
array([ 1.61803399, 2.61803399])
球形贝塞尔零点
球形贝塞尔零点
例如,如果你想计算球形电磁腔的本征频率,找出球形贝塞尔函数的零点可能是有用的(在这种情况下,你还需要(r*Jn(r))导数的零点)。
问题是你必须计算出你应该找到零的范围。
令人高兴的是,第 n 个球面贝塞尔函数的给定零点的范围可以从第(n-1)个球面贝塞尔函数的零点计算出来。
因此,这里提出的方法是递归的,知道 0 阶球面贝塞尔函数等于 sin(r)/r,其零点是众所周知的。
这种方法显然一点也不高效,但它很有效;-).
使用[:Cookbook/Matplotlib:Matplotlib]显示了一个示例,用于 5 阶球面贝塞尔函数的前 10 个零点(以及(r*J5(r))的导数)。
#! /usr/bin/env python
### recursive method: computes zeros ranges of Jn(r,n) from zeros of Jn(r,n-1)
### (also for zeros of (rJn(r,n))')
### pros : you are certain to find the right zeros values;
### cons : all zeros of the n-1 previous Jn have to be computed;
### note : Jn(r,0) = sin(r)/r
from scipy import arange, pi, sqrt, zeros
from scipy.special import jv, jvp
from scipy.optimize import brentq
from sys import argv
from pylab import *
def Jn(r,n):
return (sqrt(pi/(2*r))*jv(n+0.5,r))
def Jn_zeros(n,nt):
zerosj = zeros((n+1, nt), dtype=Float32)
zerosj[0] = arange(1,nt+1)*pi
points = arange(1,nt+n+1)*pi
racines = zeros(nt+n, dtype=Float32)
for i in range(1,n+1):
for j in range(nt+n-i):
foo = brentq(Jn, points[j], points[j+1], (i,))
racines[j] = foo
points = racines
zerosj[i][:nt] = racines[:nt]
return (zerosj)
def rJnp(r,n):
return (0.5*sqrt(pi/(2*r))*jv(n+0.5,r) + sqrt(pi*r/2)*jvp(n+0.5,r))
def rJnp_zeros(n,nt):
zerosj = zeros((n+1, nt), dtype=Float32)
zerosj[0] = (2.*arange(1,nt+1)-1)*pi/2
points = (2.*arange(1,nt+n+1)-1)*pi/2
racines = zeros(nt+n, dtype=Float32)
for i in range(1,n+1):
for j in range(nt+n-i):
foo = brentq(rJnp, points[j], points[j+1], (i,))
racines[j] = foo
points = racines
zerosj[i][:nt] = racines[:nt]
return (zerosj)
n = int(argv[1]) # n'th spherical bessel function
nt = int(argv[2]) # number of zeros to be computed
dr = 0.01
eps = dr/1000
jnz = Jn_zeros(n,nt)[n]
r1 = arange(eps,jnz[len(jnz)-1],dr)
jnzp = rJnp_zeros(n,nt)[n]
r2 = arange(eps,jnzp[len(jnzp)-1],dr)
grid(True)
plot(r1,Jn(r1,n),'b', r2,rJnp(r2,n),'r')
title((str(nt)+' first zeros'))
legend((r'$j_{'+str(n)+'}(r)$', r'$(rj_{'+str(n)+'}(r))\'$'))
plot(jnz,zeros(len(jnz)),'bo', jnzp,zeros(len(jnzp)),'rd')
gca().xaxis.set_minor_locator(MultipleLocator(1))
# gca().xaxis.set_minor_formatter(FormatStrFormatter('%d'))
show()
bessph_zeros_rec 5 10
附件
Numpy 和 Scipy /提示和技巧
Numpy 和 Scipy /提示和技巧
按名称寻址数组列
按名称寻址数组列
通过名称访问数组列有两种非常密切相关的方法:recarrays 和结构化数组。结构化数组只是具有复杂数据类型的数组:
#!python numbers=disable
In [1]: from numpy import *
In [2]: ones(3, dtype=dtype([('foo', int), ('bar', float)]))
Out[2]:
array([(1, 1.0), (1, 1.0), (1, 1.0)],
dtype=[('foo', '<i4'), ('bar', '<f8')])
In [3]: r = _
In [4]: r['foo']
Out[4]: array([1, 1, 1])
recarray 是 ndarray 的一个子类,它只是将属性访问权限添加到结构化数组中:
#!python numbers=disable
In [10]: r2 = r.view(recarray)
In [11]: r2
Out[11]:
recarray([(1, 1.0), (1, 1.0), (1, 1.0)],
dtype=[('foo', '<i4'), ('bar', '<f8')])
In [12]: r2.foo
Out[12]: array([1, 1, 1])
recarrays 的一个缺点是属性访问特性会减慢所有字段访问,甚至是 r['foo']形式,因为它会在中间插入一堆纯 Python 代码。许多代码不会注意到这一点,但是如果您最终不得不遍历一个记录数组,这将是您的一个热点。
结构化数组有时被混乱地称为记录数组。
. - lightly adapted from a Robert Kern post of Thu, 26 Jun 2008 15:25:11 -0500
构建数组
构建数组
这是对数组对象、它们在 scipy 中的声明和使用的简单介绍。在带文档的 Numpy 示例列表中可以找到数组 Numpy 函数示例的完整列表
基础
标准 python 语言中尚未定义数字数组。要将数组对象及其方法加载到命名空间中,必须导入 numpy 包:
from numpy import *
可以使用数组函数从通常的 python 列表和元组中创建数组。例如,
a = array([1,2,3])
返回一维整数数组。数组实例附带了一大组方法和属性。例如,是数组的维数。在这种情况下,它只是。
数组对象和 python 的序列对象之间的一个很大的区别是数学运算符的定义。两个列表的添加将这些列表串联起来,而两个数组的添加将数组元素化。例如:
b = array((10,11,12))
a + b
array([11, 13, 15])
减法、乘法和除法的定义相似。
对于初学者来说,一个常见的问题是数组的类型定义。除非另有指示,否则数组构造使用其参数的类型。因为是从整数列表创建的,所以它被定义为整数数组,更准确地说:
a.dtype
dtype('int64')
因此,除法等数学运算在 python 中会照常运行,即会返回一个整数答案:
a/3
array([0, 0, 1])
为了获得预期的答案,一种解决方案是通过除以一个实数来强制将整数转换为实数。更谨慎的方法是在初始化时定义类型:
a = array([1,2,3], dtype=float)
另一种转换方式是使用 Numpy 的内置转换函数作为 type 和 cast。这些选项允许您更改正在使用的数据类型:
a = array([1,2,3], dtype=int)
b = a.astype('float')
数组的元素使用括号符号来访问,其中是从 0 开始的整数索引。子数组可以通过使用start:stop:step
形式的通用索引来访问。a[start:stop:step]
将返回对数组a
的子数组的引用,从(包括)索引start
处的元素开始,到(不包括)在步骤的索引停止处的元素。例如:
data = array([0.5, 1.2, 2.2, 3.4, 3.5, 3.4, 3.4, 3.4], float)
t = arange(len(data), dtype='float') * 2*pi/(len(data)-1)
t[:] # get all t-values
array([ 0\. , 0.8975979 , 1.7951958 , 2.6927937 , 3.5903916 ,
4.48798951, 5.38558741, 6.28318531])
t[2:4] # get sub-array with the elements at the indexes 2,3
array([ 1.7951958, 2.6927937])
t[slice(2,4)] # the same using slice
array([ 1.7951958, 2.6927937])
t[0:6:2] # every even-indexed value up to but excluding 6
array([ 0\. , 1.7951958, 3.5903916])
此外,有可能使用布尔数组访问数组元素。布尔数组将要访问的元素的索引设置为真。
i = array(len(t)*[False], bool) # create an bool-array for indexing
i[2] = True; i[4] = True; i[6] = True # we want elements with indexes 2,4 and 6
t[i]
array([ 1.7951958 , 3.5903916 , 5.38558741])
我们可以使用这个语法来制作稍微复杂一点的构造。考虑之前定义的data[:]
和t[:]
数组。假设我们想要得到四个(t[i]/data[i]
)-与最靠近一个点p=1.8
的四个t[i]-values
配对。我们可以按如下方式进行:
p=1.8 # set our point
abs(t-p) # how much do the t[:]-values differ from p?
array([ 1.8 , 0.9024021 , 0.0048042 , 0.8927937 , 1.7903916 ,
2.68798951, 3.58558741, 4.48318531])
dt_m = sort(abs(t-p))[3] # how large is the 4-th largest absolute distance between the
# t[:]-values and p
abs(t-p) <= dt_m # where are the four elements of t[:]closest to p ?
array([False, True, True, True, True, False, False, False], dtype=bool)
y_p = data[abs(t-p) <= dt_m] # construct the sub-arrays; (1) get the 4 t[:]-values
t_p = t[abs(t-p) <= dt_m] # (2) get the data t[:]-values corresponding to the 4 t[:] values
y_p
array([ 1.2, 2.2, 3.4, 3.5])
t_p
array([ 0.8975979, 1.7951958, 2.6927937, 3.5903916])
必须记住,切片返回对数据的引用。因此,返回的子数组中的更改会导致原始数组中的更改,反之亦然。如果只想复制值,可以使用矩阵对象的 copy()-方法。例如:
# first lets define a 2-d matrix
A = array([[0, 1, 2, 3], # initialize 2-d array
[4, 5, 6, 7],
[8, 9, 10, 11],
[12, 13, 14, 15]])
A
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
b=A[1:3,0:2] # let's get a sub-matrix containing the cross-section of
# rows 1,2 and columns 0,1
# !attention! this assigns to b a reference to the
# sub-matrix of A
b
array([[4, 5],
[8, 9]])
c=A[1:3,0:2].copy() # copy the entries
c
array([[4, 5],
[8, 9]])
A[1:3,0:2] = 42 # we can also assign by slicing (this also changes shallow copies)
b # b also affected (only a reference to sub matrix)
array([[42, 42],
[42, 42]])
c # still the same (deep copy)
array([[4, 5],
[8, 9]])
矩阵点积
下一个示例创建两个矩阵:a
和b
,并计算点积axb
(换句话说,标准矩阵积)
a = array([[1,2], [2,3]])
b = array([[7,1], [0,1]])
dot(a, b)
array([[ 7, 3],
[14, 5]])
自动数组创建
Scipy(通过 Numpy)提供了许多自动创建数组的方法。例如,要创建一个均匀间隔的数字向量,可以调用 linspace 函数。这对于计算某个域上函数的结果通常很有用。例如,要计算一个周期上的函数值,我们将定义一个从 0 到 2π的向量,并计算该向量中所有值的函数值:
x = linspace(0, 2*pi, 100)
y = sin(x)
使用类及其一些对象创建方法和,可以在 N 维网格上完成相同的操作。例如,
x, y = mgrid[0:10:.1, 0:10:.2]
返回两个矩阵,x 和 y,它们的元素分别以. 1 和. 2 的增量从 0 到 10(不包括 0 和 10)。这些矩阵可用于计算由这些网格定义的点(x_i,y_i)处的函数值:
z = (x+y)**2
ogrid 对象具有完全相同的行为,但是它没有将 N-D 矩阵存储到内存中,而是只存储定义它的一维向量。对于大型矩阵,这可以显著节省存储空间。
创建矩阵的其他有用函数是和,它们初始化满是 0 和 1 的数组。请注意,默认情况下,这些将是浮点数组。这可能会导致不知情者的奇怪行为。例如,让我们用零初始化一个矩阵,然后逐个元素地在其中放置值。
mz = zeros((2, 2), dtype=int)
mz[0, 0] = .5**2
mz[1, 1] = 1.6**2
在本例中,我们试图将浮点数存储在整数数组中。因此,数字然后被重铸为整数,因此,如果我们打印矩阵,我们得到:
mz
array([[0, 0],
[0, 2]])
要创建实数数组,只需在函数调用中显式声明类型:
mz = zeros((2, 2), dtype=float)
重复数组段
ndarray.repeat()方法返回一个新数组,其维数与旧数组相同。
a = array([[0, 1],
... [2, 3]])
a.repeat(2, axis=0) # repeats each row twice in succession
array([[0, 1],
[0, 1],
[2, 3],
[2, 3]])
a.repeat(3, axis=1) # repeats each column 3 times in succession
array([[0, 0, 0, 1, 1, 1],
[2, 2, 2, 3, 3, 3]])
a.repeat(2, axis=None) # flattens (ravels), then repeats each element twice
array([0, 0, 1, 1, 2, 2, 3, 3])
这些可以结合起来做一些有用的事情,比如放大存储在 2D 数组中的图像数据:
def enlarge(a, x=2, y=None):
"""Enlarges 2D image array a using simple pixel repetition in both dimensions.
Enlarges by factor x horizontally and factor y vertically.
If y is left as None, uses factor x for both dimensions."""
a = asarray(a)
assert a.ndim == 2
if y == None:
y = x
for factor in (x, y):
assert factor.__class__ == int
assert factor > 0
return a.repeat(y, axis=0).repeat(x, axis=1)
enlarge(a, x=2, y=2)
array([[0, 0, 1, 1],
[0, 0, 1, 1],
[2, 2, 3, 3],
[2, 2, 3, 3]])
类卷积运算
类卷积运算
用户经常希望将数组分解成重叠的块,然后对每个块应用相同的操作。例如,您可以通过对每个块进行快速傅立叶变换来生成动态功率谱,或者您可以使用点积来构建卷积。其中一些操作已经存在于 numpy 和 scipy 中,但其他操作不存在。解决这个问题的一种方法是制作一个矩阵,其中每一列都是一个起始位置,每一行都是一个块。这通常需要复制一些数据,如果有很多重叠,可能会有很多数据,但是 numpy 的大步可以用来做到这一点。大步的简化不是免费的;如果修改数组,所有共享元素都将被修改。然而,这是一个有用的操作。找到附上的代码, segmentaxis.py
。示例用法:
In [1]: import numpy as N
In [2]: import segmentaxis
In [3]: a = N.zeros(30)
In [4]: a[15] = 1
In [5]: filter = N.array([0.1,0.5,1,0.5,0.1])
In [6]: sa = segmentaxis.segment_axis(a,len(filter),len(filter)-1)
In [7]: sa
Out[7]:
array([[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 1.],
[ 0., 0., 0., 1., 0.],
[ 0., 0., 1., 0., 0.],
[ 0., 1., 0., 0., 0.],
[ 1., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.]])
In [8]: N.dot(sa[::2,:],filter)
Out[8]:
array([ 0\. , 0\. , 0\. , 0\. , 0\. , 0\. , 0.5, 0.5, 0\. , 0\. , 0\. ,
0\. , 0\. ])
In [9]: N.dot(sa[1::2,:],filter)
Out[9]:
array([ 0\. , 0\. , 0\. , 0\. , 0\. , 0.1, 1\. , 0.1, 0\. , 0\. , 0\. ,
0\. , 0\. ])
In [10]: N.dot(sa,filter)
Out[10]:
array([ 0\. , 0\. , 0\. , 0\. , 0\. , 0\. , 0\. , 0\. , 0\. , 0\. , 0\. ,
0.1, 0.5, 1\. , 0.5, 0.1, 0\. , 0\. , 0\. , 0\. , 0\. , 0\. ,
0\. , 0\. , 0\. , 0\. ])
附件
索引 numpy 数组
索引 numpy 数组
numpy 的重点是引入一个多维数组对象来保存均匀类型的数值数据。这当然是一个存储数据的有用工具,但也有可能在不编写低效 python 循环的情况下操作大量值。为了实现这一点,人们需要能够以许多不同的方式引用数组的元素,从简单的“切片”到使用数组作为查找表。本页的目的是浏览各种不同类型的索引。希望有时特殊的语法也会变得更加清晰。
我们将尽可能使用相同的数组作为示例:
import numpy as np
A = np.arange(10)
A
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
B = np.reshape(np.arange(9),(3,3))
B
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
C = np.reshape(np.arange(2*3*4),(2,3,4))
C
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
元素
挑选数组的一个或一些元素的最简单方法看起来非常类似于 python 列表:
A[1]
1
B[1,0]
3
C[1,0,2]
14
也就是说,要挑选出一个特定的元素,只需将索引放在它后面的方括号中。作为 python 的标准,元素号从零开始。
如果要就地更改数组值,只需在赋值中使用上面的语法:
T = A.copy()
T[3] = -5
T
array([ 0, 1, 2, -5, 4, 5, 6, 7, 8, 9])
T[0] += 7
T
array([ 7, 1, 2, -5, 4, 5, 6, 7, 8, 9])
(与的业务。copy()是为了确保我们实际上不会修改 A,因为那样会使进一步的示例变得混乱。)注意,numpy 还支持 python 的“增广赋值”运算符,+=,-=,*=,等等。
请注意,数组元素的类型是数组本身的属性,因此如果您尝试将另一种类型的元素分配给数组,它将被静默转换(如果可能的话):
T = A.copy()
T[3] = -1.5
T
array([ 0, 1, 2, -1, 4, 5, 6, 7, 8, 9])
T[3] = -0.5j
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-513dc3e86c66> in <module>()
----> 1 T[3] = -0.5j
TypeError: can't convert complex to long
T
array([ 0, 1, 2, -1, 4, 5, 6, 7, 8, 9])
请注意,发生的转换是默认转换;在浮点到 int 的转换中,它是截断的。如果你想要不同的东西,比如说发言,你必须自己安排(例如用 np.floor())。在将复数值转换为整数的情况下,没有合理的默认方法,因此 numpy 会引发异常并保持数组不变。
最后,还有两个稍微技术性的问题。
如果您想以编程方式操作索引,您应该知道当您编写类似
C[1,0,1]
13
它与(实际上它在内部转换为)相同
C[(1,0,1)]
13
这种看起来奇特的语法是构造一个元组,python 中不可变序列的数据结构,并将该元组用作数组的索引。(引擎盖下 C[1,0,1]转换为 C. getitem ((1,0,1))。)这意味着如果您愿意,您可以创建元组:
i = (1,0,1)
C[i]
13
如果您似乎不想这样做,可以考虑迭代任意多维数组:
for i in np.ndindex(B.shape):
print i, B[i]
(0, 0) 0
(0, 1) 1
(0, 2) 2
(1, 0) 3
(1, 1) 4
(1, 2) 5
(2, 0) 6
(2, 1) 7
(2, 2) 8
当我们开始研究花式索引和函数 np.where()时,用元组进行索引也会变得很重要。
我想提到的最后一个技术问题是,当您从数组中选择一个元素时,您得到的是与数组元素相同的类型。这听起来可能很明显,从某种程度上来说也是如此,但是请记住,即使是像我们的 A、B 和 C 这样无害的 numpy 数组也经常包含不太像 python 类型的类型:
a = C[1,2,3]
a
23
type(a)
numpy.int64
type(int(a))
int
a**a
-c:1: RuntimeWarning: overflow encountered in long_scalars
8450172506621111015
int(a)**int(a)
20880467999847912034355032910567L
为了一致性,numpy 标量也支持某些索引操作,但这些操作有些微妙,正在讨论中。
部分
能够处理数组的单个元素显然是非常重要的。但是 numpy 的卖点之一是“数组式”操作的能力:
2*A
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
这很方便,但是人们经常希望只处理数组的一部分。例如,假设要计算 A 的差的数组,即元素为 A[1]-A[0]、A[2]-A[1]等的数组。(事实上,函数 np.diff 做到了这一点,但是为了方便说明,我们忽略它。)numpy 使得使用数组操作来实现这一点成为可能:
A[1:]
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
A[:-1]
array([0, 1, 2, 3, 4, 5, 6, 7, 8])
A[1:] - A[:-1]
array([1, 1, 1, 1, 1, 1, 1, 1, 1])
这是通过制作一个除了 A 的第一个元素以外的数组,一个除了 A 的最后一个元素以外的数组,并减去相应的元素来实现的。这样取子阵的过程叫做“切片”。
一维切片
切片的一般语法是数组 [ 开始 : 停止 : 步骤 ]。开始、停止、步骤中的任意一个或全部值可能被遗漏(如果步骤被遗漏,其前面的冒号也可能被遗漏):
A[5:]
array([5, 6, 7, 8, 9])
A[:5]
array([0, 1, 2, 3, 4])
A[::2]
array([0, 2, 4, 6, 8])
A[1::2]
array([1, 3, 5, 7, 9])
A[1:8:2]
array([1, 3, 5, 7])
和 python 一样,开始指数包含在内,停止指数不包含在内。和 python 一样,负数表示从数组末尾开始或停止向后计数:
A[-3:]
array([7, 8, 9])
A[:-3]
array([0, 1, 2, 3, 4, 5, 6])
如果在数组中停止先于开始,则返回长度为零的数组:
A[5:3]
array([], dtype=int64)
(“dtype=int32”出现在打印形式中,因为在没有元素的数组中,人们无法从它们的打印表示中分辨出元素的类型。尽管如此,如果数组中有任何元素,跟踪它们的类型还是有意义的。)
如果指定一个恰好只有一个元素的切片,则得到一个恰好只有一个元素的数组:
A[5:6]
array([5])
A[5]
5
这似乎是相当明显和合理的,但当处理花哨的索引和多维数组时,这可能会令人惊讶。
如果数字步长为负,则通过数组的步长为负,也就是说,新数组以相反的顺序包含(一些)原始数组的元素:
A[::-1]
array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
这是非常有用的,但是当给出启动和停止时,可能会令人困惑:
A[5:3:-1]
array([5, 4])
A[3:5:1]
array([3, 4])
要记住的规则是:无论步是正还是负,开始总是包括在内,停止从来不是。
正如可以将数组中的元素作为子数组而不是逐个进行检索一样,可以将它们作为子数组而不是逐个进行修改:
#!python numbers=disable
>>> T = A.copy()
>>> T
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> T[1::2]
array([1, 3, 5, 7, 9])
>>> T[1::2] = -np.arange(5)
>>> T[1::2]
array([ 0, -1, -2, -3, -4])
>>> T
array([ 0, 0, 2, -1, 4, -2, 6, -3, 8, -4])
如果您试图分配的数组是错误的形状,则会引发异常:
#!python numbers=disable
>>> T = A.copy()
>>> T[1::2] = np.arange(6)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: shape mismatch: objects cannot be broadcast to a single shape
>>> T[:4] = np.array([[0,1],[1,0]])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: shape mismatch: objects cannot be broadcast to a single shape
如果你认为错误信息听起来令人困惑,我不得不同意,但这是有原因的。在第一种情况下,我们试图将六个元素填充到五个插槽中,因此 numpy 拒绝了。在第二种情况下,有正确数量的元素——四个——但是我们试图填充一个二乘二的数组,这里应该有一个长度为四的一维数组。虽然 numpy 可以将二乘二的数组强制转换成正确的形状,但是设计者选择遵循 python 的哲学“显式比隐式好”,并将任何强制留给用户。不过,让我们这么做吧:
#!python numbers=disable
>>> T = A.copy()
>>> T[:4] = np.array([[0,1],[1,0]]).ravel()
>>> T
array([0, 1, 1, 0, 4, 5, 6, 7, 8, 9])
因此,为了让分配发挥作用,仅仅有正确数量的元素是不够的——它们必须排列成正确形状的数组。
还有一个问题使错误信息复杂化:numpy 有一些非常方便的规则,用于将低维数组转换为高维数组,以及沿轴隐式重复数组。这个过程叫做“广播”。我们将在其他地方看到更多,但这里它是最简单的形式:
#!python numbers=disable
>>> T = A.copy()
>>> T[1::2] = -1
>>> T
array([ 0, -1, 2, -1, 4, -1, 6, -1, 8, -1])
我们告诉 numpy 取一个标量,-1,并将其放入长度为 5 的数组中。numpy 的广播规则不是发出错误信号,而是告诉它通过重复标量五次,将这个标量转换成长度为五的有效数组。(当然,它实际上不会创建这样大小的临时数组;事实上,它使用了一个巧妙的技巧,告诉自己临时数组的元素间隔为零字节。)这种特殊的广播情况一直在使用:
#!python numbers=disable
>>> T = A.copy()
>>> T[1::2] -= 1
>>> T
array([0, 0, 2, 2, 4, 4, 6, 6, 8, 8])
分配有时是使用“一切”部分的好理由:
#!python numbers=disable
>>> T = A.copy()
>>> T[:] = -1
>>> T
array([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
>>> T = A.copy()
>>> T = -1
>>> T
-1
这里发生了什么?在第一种情况下,我们告诉 numpy 将-1 赋给 T 的所有元素,这就是它所做的。第二种情况,我们告诉 python“T =-1”。在 python 中,变量只是可以附加到内存中对象的名称。这与 C 语言形成鲜明对比,在 C 语言中,变量是可以存储数据的命名内存区域。对变量名的赋值——在这种情况下是 T——只是改变名称所指的对象,而不会以任何方式改变底层对象。(如果名称是对原始对象的唯一引用,那么在重新分配之后,您的程序就不可能再找到它了,所以 python 会删除原始对象来释放一些内存。)在像 C 语言这样的语言中,给变量赋值会改变存储在内存区域中的值。如果真的一定要用 C 语言来思考,可以把所有 python 变量都看作是持有指向实际对象的指针;对 python 变量的赋值只是指针的修改,并不影响指向的对象(除非垃圾收集删除它)。无论如何,如果你想修改一个数组的内容,你不能通过给数组赋一个名字来完成;您必须使用切片分配或其他方法。
最后,一个技术问题:程序如何以编程方式处理切片?如果您想保存一个切片规范,以便以后应用于许多数组,该怎么办?答案是使用切片对象,它是使用 slice()构造的:
#!python numbers=disable
>>> A[1::2]
array([1, 3, 5, 7, 9])
>>> s = slice(1,None,2)
>>> A[s]
array([1, 3, 5, 7, 9])
(遗憾的是,你不能只写“s = 1::2”。但是在方括号内,1::2 在内部转换为切片(1,无,2)。)您可以像使用冒号符号一样,将参数省略到 slice()中,但有一个例外:
#!python numbers=disable
>>> A[slice(-3)]
array([0, 1, 2, 3, 4, 5, 6])
>>> A[slice(None,3)]
array([0, 1, 2])
>>> A[slice()]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: slice expected at least 1 arguments, got 0
>>> A[slice(None,None,None)]
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
多维切片
一维数组非常有用,但是通常一个人拥有的数据是自然多维的——例如,图像数据可能是像素值的 N 乘 M 数组,或者是颜色值的 N 乘 M 乘 3 数组。正如获取一维数组的切片很有用一样,获取多维数组的切片也很有用。这相当简单:
#!python numbers=disable
>>> B
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
>>> B[:2,:]
array([[0, 1, 2],
[3, 4, 5]])
>>> B[:,::-1]
array([[2, 1, 0],
[5, 4, 3],
[8, 7, 6]])
本质上,我们只需为每个轴指定一个一维切片。也可以为轴而不是切片提供一个数字:
#!python numbers=disable
>>> B[0,:]
array([0, 1, 2])
>>> B[0,::-1]
array([2, 1, 0])
>>> B[:,0]
array([0, 3, 6])
请注意,当人们为第一个轴提供一个数字时,结果不再是二维数组;现在是一维数组。这很有道理:
#!python numbers=disable
>>> B[:,:]
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
>>> B[0,:]
array([0, 1, 2])
>>> B[0,0]
0
如果你不提供数字,你会得到一个二维数组;如果你提供一个数字,维度下降一,你得到一个一维数组;如果你提供两个数字,量纲减 2,你得到一个标量。(如果你认为你应该得到一个零维数组,那你是在打开一罐虫子。标量和零维数组之间的区别或缺乏区别是一个正在讨论和开发的问题。)
如果您习惯于使用矩阵,您可能希望保留“行向量”和“列向量”之间的区别。numpy 只支持一种一维数组,但是您可以将行和列向量表示为两个维数组,其中一个维度恰好为 1。不幸的是,这些对象的索引变得很麻烦。
与一维数组一样,如果您指定一个恰好只有一个元素的切片,您将得到一个数组,其中一个轴的长度为 1 -该轴不会像您为该轴提供实际数字时那样“消失”:
#!python numbers=disable
>>> B[:,0:1]
array([[0],
[3],
[6]])
>>> B[:,0]
array([0, 3, 6])
numpy 还有一些快捷方式,非常适合处理维数不确定的数组。如果这看起来不合理,请记住 numpy 的许多函数(例如 np.sort()、np.sum()和 np.transpose())必须在任意维度的数组上工作。当然可以从数组中提取维数并显式地使用它,但是一个人的代码往往会充满像(slice(None,None,None),)*(C.ndim-1)这样的东西,读起来很不舒服。所以 numpy 有一些捷径可以简化事情。
首先是省略号对象:
#!python numbers=disable
>>> A[...]
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> B[...]
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
>>> B[0,...]
array([0, 1, 2])
>>> B[0,...,0]
array(0)
>>> C[0,...,0]
array([0, 4, 8])
>>> C[0,Ellipsis,0]
array([0, 4, 8])
省略号(三个点)表示“需要多少就有多少”:”。(它在索引篡改代码中使用的名称是省略号,它不是 numpy 特定的。)这使得只操作数组的一个维度变得很容易,让 numpy 在“不需要的”维度上进行数组操作。在任何给定的索引表达式中,您实际上只能有一个省略号,否则表达式对每个省略号中应该放多少个“:”会有歧义。(事实上,出于某种原因,允许有类似“C[...,...]";这实际上并不含糊。)
在某些情况下,完全省略省略号是很方便的:
#!python numbers=disable
>>> B[0]
array([0, 1, 2])
>>> C[0]
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> C[0,0]
array([0, 1, 2, 3])
>>> B[0:2]
array([[0, 1, 2],
[3, 4, 5]])
如果没有为数组提供足够的索引,将会默默添加一个省略号。这意味着在某种意义上,您可以将二维数组视为一维数组的数组。结合 numpy 的数组操作,这意味着为一维数组编写的函数通常只适用于二维数组。例如,回想一下我们在一维切片一节中写的差分运算:
#!python numbers=disable
>>> A[1:] - A[:-1]
array([1, 1, 1, 1, 1, 1, 1, 1, 1])
>>> B[1:] - B[:-1]
array([[3, 3, 3],
[3, 3, 3]])
它可以不加修改地沿着二维数组的第一个轴取差。
写入多维切片的工作方式与写入一维切片的工作方式相同:
>>> T = B.copy()
>>> T[1,:] = -1
>>> T
array([[ 0, 1, 2],
[-1, -1, -1],
[ 6, 7, 8]])
>>> T[:,:2] = -2
>>> T
array([[-2, -2, 2],
[-2, -2, -1],
[-2, -2, 8]])
FIXME: np.newaxis 和广播规则。
视图与副本
零维数组,单个元素的视图。
花式索引
切片非常方便,而且它们可以创建为视图的事实使它们非常高效。但是有些操作真的不能用切片来完成;例如,假设一个人想要对一个数组中的所有负值求平方。除了用 python 编写一个循环之外,人们还希望能够找到负值,提取它们,将其平方,并将新值放在旧值所在的位置:
#!python numbers=disable
>>> T = A.copy() - 5
>>> T[T<0] **= 2
>>> T
array([25, 16, 9, 4, 1, 0, 1, 2, 3, 4])
或者假设你想使用一个数组作为查找表,也就是说,对于一个数组 B,产生一个数组,它的第 I,j 个元素是 LUT[B[i,j]]: FIXME: argsort 是一个更好的例子
#!python numbers=disable
>>> LUT = np.sin(A)
>>> LUT
array([ 0\. , 0.84147098, 0.90929743, 0.14112001, -0.7568025 ,
-0.95892427, -0.2794155 , 0.6569866 , 0.98935825, 0.41211849])
>>> LUT[B]
array([[ 0\. , 0.84147098, 0.90929743],
[ 0.14112001, -0.7568025 , -0.95892427],
[-0.2794155 , 0.6569866 , 0.98935825]])
对于这类事情,numpy 提供了所谓的“花式索引”。它不像切片那样快速和轻量级,但它允许人们做一些相当复杂的事情,同时让 numpy 在 c 语言中做所有的艰苦工作。
布尔索引
经常发生的情况是,人们只想选择或修改满足某些条件的数组元素。numpy 提供了几种工具来处理这种情况。首先是布尔数组。numpy 数组之间的比较(等于、小于等)生成布尔值数组:
#!python numbers=disable
>>> A<5
array([ True, True, True, True, True, False, False, False, False, False], dtype=bool)
这些是普通的数组。实际的存储类型通常是每个值一个字节,而不是打包成一个字节的位,但是布尔数组提供了与其他数组相同的索引和数组操作范围。不幸的是,python 的“and”和“or”不能被覆盖来执行数组式操作,因此必须使用按位操作“&”、“|”和“^”(用于异或)。同样,python 的链式不等式不能被覆盖。此外,令人遗憾的是,我们无法更改按位运算符的优先级:
#!python numbers=disable
>>> c = A<5 & A>1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> c = (A<5) & (A>1)
>>> c
array([False, False, True, True, True, False, False, False, False, False], dtype=bool)
然而,numpy 的布尔数组非常强大。
可以使用布尔数组从数组中提取值:
#!python numbers=disable
>>> c = (A<5) & (A>1)
>>> A[c]
array([2, 3, 4])
结果必然是原始数组的副本,而不是视图,因为通常情况下,c 的 True 元素不会选择均匀分布的内存布局。然而,也可以使用布尔数组来写入特定元素:
>>> T = A.copy()
>>> c = (A<5) & (A>1)
>>> T[c] = -7
>>> T
array([ 0, 1, -7, -7, -7, 5, 6, 7, 8, 9])
FIXME:提到哪里()
多维布尔索引
布尔索引也适用于多维数组。在其最简单(也是最常见)的体现中,您只需提供一个布尔数组作为索引,其形状与原始数组相同:
>>> C[C%5==0]
array([ 0, 5, 10, 15, 20])
然后,您将获得条件为真的元素的一维数组。(请注意,数组必须是一维的,因为布尔值可以围绕数组任意排列。如果您想跟踪原始数组中值的排列,请考虑使用 numpy 的“屏蔽数组”工具。)您也可以使用布尔索引进行赋值,就像对一维数组一样。
布尔数组上两个非常有用的操作是 np.any()和 np.all():
>>> np.any(B<5)
True
>>> np.all(B<5)
False
他们只做他们在 tin 上说的事情,评估布尔矩阵中的任何条目是否为真,或者布尔矩阵中的所有元素是否为真。但是它们也可以用于“沿轴”求值,例如,生成一个布尔数组,说明给定行中的任何元素是否为真:
>>> B<5
array([[ True, True, True],
[ True, True, False],
[False, False, False]], dtype=bool)
>>> np.any(B<5, axis=1)
array([ True, True, False], dtype=bool)
>>> np.all(B<5, axis=1)
array([ True, False, False], dtype=bool)
还可以使用布尔索引来拉出符合某些标准的行或列:
>>> B[np.any(B<5, axis=1),:]
array([[0, 1, 2],
[3, 4, 5]])
这里的结果是二维的,因为布尔索引的结果是一维的,因为每行都是一维的。
这也适用于高维布尔数组:
>>> c = np.any(C<5,axis=2)
>>> c
array([[ True, True, False],
[False, False, False]], dtype=bool)
>>> C[c,:]
array([[0, 1, 2, 3],
[4, 5, 6, 7]])
这里的结果也是二维的,尽管这可能有点令人惊讶。布尔数组是二维的,但是对应于布尔数组的返回值部分必须是一维的,因为真值可以任意分布。每个真值或假值对应的 C 的子阵是一维的,所以我们得到一个二维的返回数组。
最后,如果您想同时对行和列应用布尔条件,请注意:
>>> B[np.array([True, False, True]), np.array([False, True, True])]
array([1, 8])
>>> B[np.array([True, False, True]),:][:,np.array([False, True, True])]
array([[1, 2],
[7, 8]])
显而易见的方法没有给出正确的答案。我不知道为什么不,或者为什么它产生了它所产生的价值。你可以通过索引两次来得到正确的答案,但是这样做既笨拙又低效,而且不允许赋值。
FIXME:出于某种原因,适用于太小的布尔数组?
位置列表索引
它以某种频率发生,人们希望在数组中的特定位置提取值。如果想要一个单一的位置,可以只使用简单的索引。但是如果有很多地方,你需要一些更聪明的东西。幸运的是,numpy 支持一种花式索引模式,可以实现这一点:
>>> primes = np.array([2,3,5,7,11,13,17,19,23])
>>> idx = [3,4,1,2,2]
>>> primes[idx]
array([ 7, 11, 3, 5, 5])
>>> idx = np.array([3,4,1,2,2])
>>> primes[idx]
array([ 7, 11, 3, 5, 5])
当您使用不是布尔数组的数组或列表进行索引时,numpy 会将其视为索引数组。数组可以是任何形状,返回的数组具有相同的形状:
>>> primes = np.array([2,3,5,7,11,13,17,19,23,29,31])
>>> primes[B]
array([[ 2, 3, 5],
[ 7, 11, 13],
[17, 19, 23]])
这实际上将原始数组用作查找表。
您也可以通过这种方式分配给数组:
>>> T = A.copy()
>>> T[ [1,3,5,0] ] = -np.arange(4)
>>> T
array([-3, 0, 2, -1, 4, -2, 6, 7, 8, 9])
警告:增广赋值——像“+=”这样的操作符是有效的,但它不一定能达到你的预期。特别是,重复的指数不会导致价值增加两次:
>>> T = A.copy()
>>> T[ [0,1,2,3,3,3] ] += 10
>>> T
array([10, 11, 12, 13, 4, 5, 6, 7, 8, 9])
这是令人惊讶、不方便和不幸的,但这是 python 如何实现“+=”运算符的直接结果。最常见的情况是类似直方图的东西:
>>> bins = np.zeros(5,dtype=np.int32)
>>> pos = [1,0,2,0,3]
>>> wts = [1,2,1,1,4]
>>> bins[pos]+=wts
>>> bins
array([1, 1, 1, 4, 0])
不幸的是,这给出了错误的答案。在旧版本的 numpy 中,没有真正令人满意的解决方案,但是从 numpy 1.1 开始,直方图函数可以做到这一点:
>>> bins = np.zeros(5,dtype=np.int32)
>>> pos = [1,0,2,0,3]
>>> wts = [1,2,1,1,4]
>>> np.histogram(pos,bins=5,range=(0,5),weights=wts,new=True)
(array([3, 1, 1, 4, 0]), array([ 0., 1., 2., 3., 4., 5.]))
FIXME:提到放()和取()
多维位置列表索引
人们也可以在多维数组上使用位置列表索引,这并不奇怪。然而,语法有点令人惊讶。假设我们想要列表[B[0,0],B[1,2],B[0,1]]。然后我们写道:
>>> B[ [0,1,0], [0,2,1] ]
array([0, 5, 1])
>>> [B[0,0],B[1,2],B[0,1]]
[0, 5, 1]
这看起来很奇怪——为什么不提供一个表示坐标的元组列表呢?原因基本上是,对于大型数组,列表和元组的效率非常低,所以 numpy 被设计为只处理数组,用于索引和值。这意味着像 B[ [(0,0),(1,2),(0,1)]这样的东西看起来就像用二维数组索引 B,正如我们在上面看到的,这只是意味着 B 应该被用作产生二维结果数组的查找表(每个结果都是一维的,就像我们通常只向二维数组提供一个索引一样)。
总之,在位置列表索引中,您为每个坐标提供一个值数组,所有形状都相同,numpy 返回一个包含通过在原始数组中查找每组坐标获得的值的相同形状的数组。如果坐标数组不是相同的形状,numpy 的广播规则将应用于它们,以尝试使它们的形状相同。如果数组没有原始数组的维数多,则原始数组被视为包含数组,多余的维数出现在结果数组上。
幸运的是,大多数时候,当你想给多维数组提供一个位置列表时,你首先会从 numpy 那里得到这个列表。正常的方法是这样的:
>>> idx = np.nonzero(B%2)
>>> idx
(array([0, 1, 1, 2]), array([1, 0, 2, 1]))
>>> B[idx]
array([1, 3, 5, 7])
>>> B[B%2 != 0]
array([1, 3, 5, 7])
这里非零()接受一个数组,并返回一个数组非零的位置列表(格式正确)。当然,也可以用布尔数组直接索引到数组中;除非非零位置的数量很少,并且索引被多次执行,否则这将更加有效。但有时直接处理指数列表是有价值的。
挑选行和列
numpy 的位置列表索引语法的一个不幸的后果是,习惯于其他数组语言的用户希望它能够挑选行和列。毕竟,想要从矩阵中拉出行和列的列表是非常合理的。所以 numpy 提供了一个方便的函数 ix_()来完成这个任务:
>>> B[ np.ix_([0,2],[0,2]) ]
array([[0, 2],
[6, 8]])
>>> np.ix_([0,2],[0,2])
(array([[0],
[2]]), array([[0, 2]]))
它的工作方式是利用 numpy 的广播设施。您可以看到用作行索引和列索引的两个数组具有不同的形状;numpy 的广播沿着太短的轴重复每一个,这样它们就符合了。
混合索引模式
当您尝试混合切片索引、元素索引、布尔索引和位置列表索引时会发生什么?
引擎盖下的索引工作原理
numpy 数组是内存块、用于解释内存位置的数据类型、大小列表和步长列表。例如,C[i,j,k]是从位置 i 步距[0]+j 步距[1]+k *步距[2]开始的元素。举例来说,这意味着转置 amatrix 可以非常有效地完成:只需反转数组的步长和大小。这就是为什么切片是有效的,可以返回视图,但花式索引较慢,不能。
在 python 级别,numpy 的索引是通过覆盖 ndarray 对象中的 getitem 和 setitem 方法来工作的。当数组被索引时调用这些方法,它们允许任意实现:
>>> class IndexDemo:
... def __getitem__(self, *args):
... print "__getitem__", args
... return 1
... def __setitem__(self, *args):
... print "__setitem__", args
... def __iadd__(self, *args):
... print "__iadd__", args
...
>>>
>>> T = IndexDemo()
>>> T[1]
__getitem__ (1,)
1
>>> T["fish"]
__getitem__ ('fish',)
1
>>> T[A]
__getitem__ (array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),)
1
>>> T[1,2]
__getitem__ ((1, 2),)
1
>>> T[1] = 7
__setitem__ (1, 7)
>>> T[1] += 7
__getitem__ (1,)
__setitem__ (1, 8)
类似数组的对象
numpy 和 scipy 还提供了一些其他类型的数组,特别是矩阵和稀疏矩阵。它们的索引与数组的索引有惊人的不同。
元数组
元数组
!元数组是一个扩展数组的类,增加了对每轴元数据存储的支持。这个类对于存储数据数组以及单位、轴名、列名、轴值等非常有用。!元数组对象可以使用命名的轴和列进行任意索引和切片。
下载到这里: MetaArray.py
示例用途
这是一个可以存储的数据类型的例子!元数组:
请注意,每个轴都是命名的,可以存储不同类型的元信息:信号轴为每个列命名了具有不同单位的列时间轴将数值与每行相关联*试验轴使用正常的整数索引
可以通过多种不同的方式访问该数组中的数据:
data[0, 1, 1]
data[:, "Voltage 1", 0]
data["Trial":1, "Signal":"Voltage 0"]
data["Time":slice(3,7)]
特征
* Per axis meta-information:`` * Named axes`` * Numerical values with units (e.g., "Time" axis above)`` * Column names/units (e.g., "Signal" axis above)``* Indexing by name:`` * Index each axis by name, so there is no need to remember order of axes`` * Within an axis, index each column by name, so there is no need to remember the order of columns``* Read/write files easily``* Append, extend, and sort convenience functions
证明文件
实例化
Accepted Syntaxes:
# Constructs MetaArray from a preexisting ndarray and info list
MetaArray(ndarray, info)
# Constructs MetaArray using empty(shape, dtype=type) and info list
MetaArray((shape), dtype=type, info)
# Constructs MetaArray from file written using MetaArray.write()
MetaArray(file='fileName')
info parameter: This parameter specifies the entire set of meta data for this !MetaArray and must follow a specific format. First, info is a list of axis descriptions:
info=[axis1, axis2, axis3...]
Each axis description is a dict which may contain:`` * "name": the name of the axis`` * "values": a list or 1D ndarray of values, one per index in the axis`` * "cols": a list of column descriptions [col1, col2, col3,...]`` * "units": the units associated with the numbers listed in "values"`` All of these parameters are optional. A column description, likewise, is a dict which may contain:`` * "name": the name of the column`` * "units": the units for all values under this column`` In the case where meta information is to apply to the entire array, (for example, if the entire array uses the same units) simply add an extra axis description to the end of the info list. All dicts may contain any extra information you want.
For example, the data set shown above would look like:
MetaArray((3, 6, 3), dtype=float, info=[
{"name": "Signal", "cols": [
{"name": "Voltage 0", "units": "V"},
{"name": "Voltage 1", "units": "V"},
{"name": "Current 0", "units": "A"}
]
},
{"name": "Time", "units": "msec", "values":[0.0, 0.1, 0.2, 0.3, 0.4, 0.5] },
{"name": "Trial"},
{"note": "Just some extra info"}
]
访问数据
可以通过多种方法访问数据:标准索引–您可以像对任何数组命名轴一样对数组进行索引–如果不记得轴的顺序,您可以这样指定要索引或切片的轴:
data["AxisName":index]
data["AxisName":slice(...)]
Note that since this syntax hijacks the original slice mechanism, you must specify a slice using slice() if you want to use named axes.`` * Column selection--If you don't remember the index of a column you wish to select, you may substitute the column's name for the index number. Lists of column names are also acceptable. For example:
data["AxisName":"ColumnName"]
data["ColumnName"] ## Works only if the named column exists for this axis
data[["ColumnName1", "ColumnName2"]]
* Boolean selection--works as you might normally expect, for example:
sel = data["ColumnName", 0, 0] > 0.2
data[sel]
* Access axis values using !MetaArray.axisValues(), or .xvals() for short. ``* Access axis units using .axisUnits(), column units using .columnUnits()``* Access any other parameter directly through the info list with .infoCopy()
文件输入输出
data.write('fileName')
newData = MetaArray(file='fileName')
性能提示
!元数组是覆盖__getitem__
和__setitem__
方法的 ndarray 的子类。由于这些方法必须为每次访问改变元信息的结构,因此与本机方法相比,它们相当慢。因此,当在上操作时,许多内置函数将运行得非常慢!元数组。因此,建议您在执行以下操作之前重铸数组:
data = MetaArray(...)
data.mean() ## Very slow
data.view(ndarray).mean() ## native speed
更多示例
A 2D array of altitude values for a topographical map might look like
info=[
{'name': 'lat', 'title': 'Latitude'},
{'name': 'lon', 'title': 'Longitude'},
{'title': 'Altitude', 'units': 'm'}
]
In this case, every value in the array represents the altitude in feet at the lat, lon`` position represented by the array index. All of the following return the `` value at lat=10, lon=5:
array[10, 5]
array['lon':5, 'lat':10]
array['lat':10][5]
Now suppose we want to combine this data with another array of equal dimensions that`` represents the average rainfall for each location. We could easily store these as two `` separate arrays or combine them into a 3D array with this description:
info=[
{'name': 'vals', 'cols': [
{'name': 'altitude', 'units': 'm'},
{'name': 'rainfall', 'units': 'cm/year'}
]},
{'name': 'lat', 'title': 'Latitude'},
{'name': 'lon', 'title': 'Longitude'}
]
We can now access the altitude values with array[0] or array['altitude'], and the`` rainfall values with array[1] or array['rainfall']. All of the following return`` the rainfall value at lat=10, lon=5:
array[1, 10, 5]
array['lon':5, 'lat':10, 'val': 'rainfall']
array['rainfall', 'lon':5, 'lat':10]
Notice that in the second example, there is no need for an extra (4th) axis description
since the actual values are described (name and units) in the column info for the first axis.
===联系人= = = = = Luke campaigna-lcampagn @ email . unc . edu
附件
多点
多点
矩阵乘法函数 numpy.dot()只接受两个参数。这意味着将两个以上的数组相乘会导致难以读取的嵌套函数调用:
dot(dot(dot(a,b),c),d)
相对于中缀符号,你只需要写
a*b*c*d
有两种方法可以定义一个“mdot”函数,它的行为类似于点,但接受两个以上的参数。使用其中一个允许您将上面的表达式写成
mdot(a,b,c,d)
使用 reduce
最简单的方法就是使用 reduce。
def mdot(*args):
return reduce(numpy.dot, args)
或者使用等效循环(这显然是 Py3K 的首选样式):
def mdot(*args):
ret = args[0]
for a in args[1:]:
ret = dot(ret,a)
return ret
这将始终为您提供从左到右的关联性,即表达式被解释为(((a*b)*c)*d)
。
您也可以制作循环的右关联版本:
def mdotr(*args):
ret = args[-1]
for a in reversed(args[:-1]):
ret = dot(a,ret)
return ret
其评估为(a*(b*(c*d)))
。但有时你希望有更好的控制,因为矩阵乘法的执行顺序会对性能产生很大影响。下一个版本提供了这种控制。
控制评估顺序
如果我们愿意牺牲 Numpy 将元组视为数组的能力,我们可以将元组用作分组构造。这个版本的mdot
允许这样的语法:
mdot(a,((b,c),d))
控制两两dot
调用的顺序。
import types
import numpy
def mdot(*args):
"""Multiply all the arguments using matrix product rules.
The output is equivalent to multiplying the arguments one by one
from left to right using dot().
Precedence can be controlled by creating tuples of arguments,
for instance mdot(a,((b,c),d)) multiplies a (a*((b*c)*d)).
Note that this means the output of dot(a,b) and mdot(a,b) will differ if
a or b is a pure tuple of numbers.
"""
if len(args)==1:
return args[0]
elif len(args)==2:
return _mdot_r(args[0],args[1])
else:
return _mdot_r(args[:-1],args[-1])
def _mdot_r(a,b):
"""Recursive helper for mdot"""
if type(a)==types.TupleType:
if len(a)>1:
a = mdot(*a)
else:
a = a[0]
if type(b)==types.TupleType:
if len(b)>1:
b = mdot(*b)
else:
b = b[0]
return numpy.dot(a,b)
乘;成倍增加;(使)繁殖
请注意,元素乘法函数numpy.multiply
与numpy.dot
具有相同的双参数限制。可以为乘法定义完全相同的广义形式。
左关联版本:
def mmultiply(*args):
return reduce(numpy.multiply, args)
def mmultiply(*args):
ret = args[0]
for a in args[1:]:
ret = multiply(ret,a)
return ret
右关联版本:
def mmultiplyr(*args):
ret = args[-1]
for a in reversed(args[:-1]):
ret = multiply(a,ret)
return ret
使用元组控制评估顺序的版本:
import types
import numpy
def mmultiply(*args):
"""Multiply all the arguments using elementwise product.
The output is equivalent to multiplying the arguments one by one
from left to right using multiply().
Precedence can be controlled by creating tuples of arguments,
for instance mmultiply(a,((b,c),d)) multiplies a (a*((b*c)*d)).
Note that this means the output of multiply(a,b) and mmultiply(a,b) will differ if
a or b is a pure tuple of numbers.
"""
if len(args)==1:
return args[0]
elif len(args)==2:
return _mmultiply_r(args[0],args[1])
else:
return _mmultiply_r(args[:-1],args[-1])
def _mmultiply_r(a,b):
"""Recursive helper for mmultiply"""
if type(a)==types.TupleType:
if len(a)>1:
a = mmultiply(*a)
else:
a = a[0]
if type(b)==types.TupleType:
if len(b)>1:
b = mmultiply(*b)
else:
b = b[0]
return numpy.multiply(a,b)
使用记录数组的对象数组
使用记录数组的对象数组
numpy 支持使用 python 对象的数组,但是这些数组缺乏普通 numpy 数组的类型一致性,因此在空间和时间方面效率非常低,并且使用起来非常麻烦。然而,能够在数组中存储用户定义的类通常是有用的。
一种方法是利用 numpy 的记录数组。这些数组中的每个元素都可以很大,因为它有命名和类型化的字段;本质上,它们相当于 C 结构的数组。因此,如果一个类包含一些数据命名的字段,每一个都是 numpy 类型,以及一些方法,那么就可以将这些对象的数组的数据表示为记录数组。获取方法更加棘手。
一种方法是创建 numpy 数组的自定义子类,该子类处理与对象类型之间的转换。其思想是将每个实例的数据存储在记录数组中,但是当索引返回标量时,从记录中的数据构造一个新的实例。类似地,当赋值给一个特定的元素时,数组子类会将一个实例转换为它的记录表示。
附件是上述方案的实现。
附件
人生游戏的跨步技巧
人生游戏的跨步技巧
这类似于[:../SegmentAxis:Segment axis],但对于带有 2D 窗口的 2D 数组。
生命的游戏是由英国数学家约翰·何顿·康威在 1970 年设计的细胞自动机,见[1]。
它由一个矩形网格组成,网格中的细胞要么是死的,要么是活的,还有一个用于更新细胞状态的转换规则。为了更新网格中的每个单元,需要检查 8 个相邻单元的状态,即希望有一种简单的方法来同时访问所有单元的 8 个相邻单元,而不需要进行不必要的复制。下面的代码片段展示了如何使用迂回的跨步技巧来达到这个目的。
[1]维基百科上的[生命游戏](http:/en.wikipedia.org/wiki/Conway's_Game_of_Life
import numpy as np
from numpy.lib import stride_tricks
x = np.arange(20).reshape([4, 5])
xx = stride_tricks.as_strided(x, shape=(2, 3, 3, 3), strides=x.strides + x.strides)
x
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
xx
array([[[[ 0, 1, 2],
[ 5, 6, 7],
[10, 11, 12]],
[[ 1, 2, 3],
[ 6, 7, 8],
[11, 12, 13]],
[[ 2, 3, 4],
[ 7, 8, 9],
[12, 13, 14]]],
[[[ 5, 6, 7],
[10, 11, 12],
[15, 16, 17]],
[[ 6, 7, 8],
[11, 12, 13],
[16, 17, 18]],
[[ 7, 8, 9],
[12, 13, 14],
[17, 18, 19]]]])
xx[0,0]
array([[ 0, 1, 2],
[ 5, 6, 7],
[10, 11, 12]])
xx[1,2]
array([[ 7, 8, 9],
[12, 13, 14],
[17, 18, 19]])
x.strides
(40, 8)
xx.strides
(40, 8, 40, 8)
类累积函数
类累积函数
伏隔,一个类似于 MATLAB 伏隔数组的函数
NumPy 不包含相当于 MATLABaccumarray
函数的函数。以下功能accum
关闭。
注意accum
可以处理 n 维数组,并且允许指定结果的数据类型。
from itertools import product
import numpy as np
def accum(accmap, a, func=None, size=None, fill_value=0, dtype=None):
"""
An accumulation function similar to Matlab's `accumarray` function.
Parameters
----------
accmap : ndarray
This is the "accumulation map". It maps input (i.e. indices into
`a`) to their destination in the output array. The first `a.ndim`
dimensions of `accmap` must be the same as `a.shape`. That is,
`accmap.shape[:a.ndim]` must equal `a.shape`. For example, if `a`
has shape (15,4), then `accmap.shape[:2]` must equal (15,4). In this
case `accmap[i,j]` gives the index into the output array where
element (i,j) of `a` is to be accumulated. If the output is, say,
a 2D, then `accmap` must have shape (15,4,2). The value in the
last dimension give indices into the output array. If the output is
1D, then the shape of `accmap` can be either (15,4) or (15,4,1)
a : ndarray
The input data to be accumulated.
func : callable or None
The accumulation function. The function will be passed a list
of values from `a` to be accumulated.
If None, numpy.sum is assumed.
size : ndarray or None
The size of the output array. If None, the size will be determined
from `accmap`.
fill_value : scalar
The default value for elements of the output array.
dtype : numpy data type, or None
The data type of the output array. If None, the data type of
`a` is used.
Returns
-------
out : ndarray
The accumulated results.
The shape of `out` is `size` if `size` is given. Otherwise the
shape is determined by the (lexicographically) largest indices of
the output found in `accmap`.
Examples
--------
>>> from numpy import array, prod
>>> a = array([[1,2,3],[4,-1,6],[-1,8,9]])
>>> a
array([[ 1, 2, 3],
[ 4, -1, 6],
[-1, 8, 9]])
>>> # Sum the diagonals.
>>> accmap = array([[0,1,2],[2,0,1],[1,2,0]])
>>> s = accum(accmap, a)
array([9, 7, 15])
>>> # A 2D output, from sub-arrays with shapes and positions like this:
>>> # [ (2,2) (2,1)]
>>> # [ (1,2) (1,1)]
>>> accmap = array([
[[0,0],[0,0],[0,1]],
[[0,0],[0,0],[0,1]],
[[1,0],[1,0],[1,1]],
])
>>> # Accumulate using a product.
>>> accum(accmap, a, func=prod, dtype=float)
array([[ -8., 18.],
[ -8., 9.]])
>>> # Same accmap, but create an array of lists of values.
>>> accum(accmap, a, func=lambda x: x, dtype='O')
array([[[1, 2, 4, -1], [3, 6]],
[[-1, 8], [9]]], dtype=object)
"""
# Check for bad arguments and handle the defaults.
if accmap.shape[:a.ndim] != a.shape:
raise ValueError("The initial dimensions of accmap must be the same as a.shape")
if func is None:
func = np.sum
if dtype is None:
dtype = a.dtype
if accmap.shape == a.shape:
accmap = np.expand_dims(accmap, -1)
adims = tuple(range(a.ndim))
if size is None:
size = 1 + np.squeeze(np.apply_over_axes(np.max, accmap, axes=adims))
size = np.atleast_1d(size)
# Create an array of python lists of values.
vals = np.empty(size, dtype='O')
for s in product(*[range(k) for k in size]):
vals[s] = []
for s in product(*[range(k) for k in a.shape]):
indx = tuple(accmap[s])
val = a[s]
vals[indx].append(val)
# Create the output array.
out = np.empty(size, dtype=dtype)
for s in product(*[range(k) for k in size]):
if vals[s] == []:
out[s] = fill_value
else:
out[s] = func(vals[s])
return out
例子
一个基本的例子——对一个 3 乘 3 的数组的对角线求和:
from numpy import array, prod
a = array([[1,2,3],[4,-1,6],[-1,8,9]])
accmap = array([[0,1,2],[2,0,1],[1,2,0]])
s = accum(accmap, a)
s
array([ 9, 7, 15])
使用乘法进行累加,从 3 乘 3 数组到 2 乘 2 数组:
accmap = array([
[[0,0],[0,0],[0,1]],
[[0,0],[0,0],[0,1]],
[[1,0],[1,0],[1,1]],
])
accum(accmap, a, func=prod, dtype=float)
array([[ -8., 18.],
[ -8., 9.]])
创建一个列表数组,其中包含要在输出数组的每个位置累加的值:
accum(accmap, a, func=lambda x: x, dtype='O')
array([[[1, 2, 4, -1], [3, 6]],
[[-1, 8], [9]]], dtype=object)
使用accum
将 1D 数组中的一些值排列在 2D 数组中(注意,使用accum
进行此操作有些过火;花哨的索引就足够了):
subs = np.array([[k,5-k] for k in range(6)])
subs
array([[0, 5],
[1, 4],
[2, 3],
[3, 2],
[4, 1],
[5, 0]])
vals = array(range(10,16))
accum(subs, vals)
array([[ 0, 0, 0, 0, 0, 10],
[ 0, 0, 0, 0, 11, 0],
[ 0, 0, 0, 12, 0, 0],
[ 0, 0, 13, 0, 0, 0],
[ 0, 14, 0, 0, 0, 0],
[15, 0, 0, 0, 0, 0]])
十九、其他例子
- 使用 NumPy 数组的 C 扩展
- 嵌入特征图形用户界面
- Matplotlib:拖放文本示例
- Matplotlib: treemap
- Mayavi:安装 python 素材来源
- 玛雅维:示例
- 用 Pyparsing 读取自定义文本文件
- 编写 Mayavi 2 脚本:基本模块
- 脚本 Mayavi 2:过滤器
- 编写 Mayavi 2 脚本:主要模块
使用数字数组的扩展
使用数字数组的扩展
我已经编写了几个处理 NumPy 数组的 C 扩展。它们很简单,但似乎很有效。他们将向您展示如何将 Python 变量和 NumPy 数组传递给您的 C 代码。一旦你学会了怎么做,那就相当简单了。我怀疑它们对于大多数数字代码来说已经足够了。我把它写成了草稿,并提供了代码和文档文件。我发现对于我的数字需求,我真的只需要传递一组有限的东西(整数、浮点、字符串和 NumPy 数组)。如果那是你的类别,这段代码可能会帮助你。
我已经测试了套路,到目前为止,还不错,但我不能保证什么。我对这个有点陌生。如果您发现任何错误,请在 SciPy 邮件列表中张贴消息。
下面给出了保存代码和文档的 tar 球的链接。
我最近更新了一些信息,包括了更多的例子。下面介绍的文件是仍然有用的原始文件。下面的链接包含最新的文档和源代码。
-露羊
对 NumPy 和 Python 的扩展
作者卢·佩科拉- 2006-12-07(草稿版本 0.1)
概观
简介–一点背景知识
在我使用 Python 的过程中,我遇到了一个典型的问题:我需要加速代码的特定部分。我不是 Python 大师或任何类型的编码/计算机大师。我使用 Python 进行数值计算,并大量使用 NumPy/NumPy。几乎每本 Python 书籍或教程都告诉你,当你需要一个例程来快速运行时,可以构建 Python 的 C 扩展。C 扩展是可以编译并链接到共享库的 C 代码,共享库可以像任何 Python 模块一样导入,您可以像调用 Python 函数一样调用指定的 C 例程。
听起来不错,但我有所保留。它看起来不平凡(在某种程度上的确如此)。于是我寻找其他的解决办法。我找到他们了。分别是 SWIG 、 Pyrex 、 ctypes 、 Psyco 、 Weave 等途径。当我尝试这些的时候,我经常得到一些简单的例子(然而不是全部)。但是当我试图将它们应用于 NumPy 时,我遇到了障碍。然后进入类型图或其他混合结构。我并不反对这些方法,但是尽管有许多在线教程和来自各种 Python 支持小组和电子邮件列表的有用建议,我永远也不会弄清楚它们并开始编写自己的代码。
所以我决定看看能不能自己写 C 扩展。康奈尔大学的汤姆·洛雷多以一些简单的 C 扩展例子的形式帮助我使用大约 2000 年编写的 Numeric。这些放在我的硬盘上,直到 5 年后出于绝望,我把它们拿出来,用他的例子,我能够很快组装几个 C 扩展(至少对我来说),处理所有我想要加速的情况(到目前为止)。这些情况主要涉及传递 Python 整数、浮点(= C doubles)、字符串以及 NumPy 1D 和 2D 浮点和整数数组。我很少需要传递其他任何东西给 C 例程来做计算。如果你和我的情况一样,那么我放在一起的这个包可能会对你有帮助。一旦你开始行动,事情就会变得相当容易。
请注意,汤姆·洛雷多不对我的代码或指令中的任何错误负责,尽管我非常感激他。同样,该代码仅用于研究。只有我的开发和使用对它进行了测试。它没有保证,也没有保修。如果存在任何生命、肢体、财产、金钱或您或他人珍视的任何东西的损失威胁,请不要使用此代码。
我使用系统 OS X 10.4(基本的 BSD Unix)、Python 2.4、NumPy 0.9x 以及 gnu 编译器和链接器 gcc,在 Macintosh G4 笔记本电脑上开发了这些 C 扩展及其 Python 包装器。我想我在这里告诉你的大部分内容将很容易被翻译成 Linux 和其他 Unix 系统,而不仅仅是 Mac。我对 Windows 不太确定。我希望我的低级方法也能让 Windows 用户轻松一些。
扩展的代码(包括 C 和 Python)可能看起来很多,但是它非常重复。一旦你得到了一个扩展函数的主要方案,你会看到它在所有其他的扩展函数中一次又一次地重复,并有微小的变化来处理不同的参数或向调用例程返回不同的对象。不要被代码所迷惑。好消息是,对于许多数字用途,扩展将遵循相同的格式,因此您可以快速重用已经为新项目编写的内容。专注于一个扩展函数,并详细遵循它(事实上,我将在下面这样做)。一旦你理解了,其他的套路就几乎显而易见了。软件包附带的几个实用函数也是如此。它们帮助您创建、测试和操作数据,并且它们还有很多重复。实用函数也非常简短,所以没什么好担心的。
NumPy 扩展的一般方案
这将在下面详细介绍,但首先我想让您了解每个扩展是如何组织的。
在 C 源文件中的 C 扩展函数之前必须做的三件事。
- 您必须包含 Python 和 NumPy 头。
- 每个扩展名必须在文件开头的定义结构中命名。这是一个用于从 Python 函数访问扩展的名称。
- 接下来进行一组初始化调用来设置 Python 和 NumPy 调用和接口。它对于所有涉及 NumPy 和 Python 的扩展都是一样的,除非您添加扩展来访问 NumPy 数组之外的其他 Python 包或类。我不会在这里讨论这些(因为我不知道)。所以 init 调用可以复制到每个扩展文件中。
每个 C 扩展将具有以下形式。
- 论点永远都是一样的:(
PyObject *self
、PyObject *args
) -如果你不知道这些到底是什么,不用担心。它们是指向一般 Python 对象的指针,并且由您将从 NumPy 和 Python 本身使用的头文件自动提供。你只需要知道这些。 - 参数通过一个函数调用进行处理,该函数调用解析它们并将它们分配给 C 定义的对象。
- 接下来,解析的结果可能会被一个实用程序检查,该程序会进入表示对象的结构,并确保数据是正确的类型(float 或 int、1D 或 2D 数组等)。).虽然我包含了一些 C 级检查,但您会发现我认为它们在用于包装 C 扩展的 Python 函数中做得更好。它们在 Python 中也容易做得多。我的调用 Python 包装器中有大量的数据检查。通常这不会导致很大的开销,因为您不会在某个循环中调用这些扩展数十亿次,而是将它们用作 C 或 C++例程的入口,以进行长时间、复杂、重复的专门计算。
- 在一些可能的数据检查之后,在实用函数的帮助下,C 数据类型被初始化为指向 NumPy 数组的数据部分。
- 接下来提取维度信息,这样您就知道列数、行数、向量维度等。
- 现在,您可以使用 C 数组来操作 NumPy 数组中的数据。上述解析中的 C 数组和 C 数据指向原始的 Python/NumPy 数据,因此在扩展返回后返回 Python 时,您所做的更改会影响数组值。你可以把数组传递给其他做计算的 C 函数,等等。请记住,您正在对原始的 NumPy 矩阵和向量进行操作。
- 在计算之后,您必须释放在为 NumPy 数组构建 C 数据时分配的任何内存。这又是由实用程序函数完成的。仅当您分配内存来处理数组时(例如在矩阵例程中),此步骤才是必需的,但是如果您没有分配内存(例如在向量例程中),则此步骤不是必需的。
- 最后,通过返回 Python 值或 NumPy 数组,返回 Python 调用函数。我有两个例子的 C 扩展。
Python 包装函数
最好通过调用 Python 函数来调用 C 扩展,然后 Python 函数调用扩展。这被称为 Python 包装函数。它让您的代码看起来更像 pythonic(例如,您可以很容易地使用关键字),并且,正如我在上面指出的,允许您在将函数参数和数据转移到 C 扩展和其他 C 函数进行大型计算之前,很容易地检查它们是否正确。这似乎是一个不必要的额外步骤,但这是值得的。
《守则》
在本节中,我参考了源文件C_arraytest.h
、C_arraytest.c
、C_arraytest.py
和C_arraytest.mak
中的代码。您应该将这些文件放在手边(可能会打印出来),这样您就可以按照下面的代码进行解释。
C 代码——实用程序的一个详细例子
首先,我将使用来自 C arraytest.h,C_arraytest.c 的代码示例,用于名为 matsq 的例程。该函数以(NumPy)矩阵 A _ A、整数 i 、(Python)浮点数 y 为输入,输出一个返回(NumPy)矩阵 B ,每个矩阵的分量等于输入矩阵分量的平方乘以浮点数的整数倍。数学上:
\(B_{ij} = i y (A_{ij})^2\)
调用 matsq 例程的 Python 代码是A=matsq(B,i,y)
。以下是一个地方的相关代码:
头文件:
/* Header to test of C modules for arrays for Python: C_test.c */
/* ==== Prototypes =================================== */
// .... Python callable Vector functions ..................
static PyObject *vecfcn1(PyObject *self, PyObject *args);
static PyObject *vecsq(PyObject *self, PyObject *args);
/* .... C vector utility functions ..................*/
PyArrayObject *pyvector(PyObject *objin);
double *pyvector_to_Carrayptrs(PyArrayObject *arrayin);
int not_doublevector(PyArrayObject *vec);
/* .... Python callable Matrix functions ..................*/
static PyObject *rowx2(PyObject *self, PyObject *args);
static PyObject *rowx2_v2(PyObject *self, PyObject *args);
static PyObject *matsq(PyObject *self, PyObject *args);
static PyObject *contigmat(PyObject *self, PyObject *args);
/* .... C matrix utility functions ..................*/
PyArrayObject *pymatrix(PyObject *objin);
double **pymatrix_to_Carrayptrs(PyArrayObject *arrayin);
double **ptrvector(long n);
void free_Carrayptrs(double **v);
int not_doublematrix(PyArrayObject *mat);
/* .... Python callable integer 2D array functions ..................*/
static PyObject *intfcn1(PyObject *self, PyObject *args);
/* .... C 2D int array utility functions ..................*/
PyArrayObject *pyint2Darray(PyObject *objin);
int **pyint2Darray_to_Carrayptrs(PyArrayObject *arrayin);
int **ptrintvector(long n);
void free_Cint2Darrayptrs(int **v);
int not_int2Darray(PyArrayObject *mat);
源文件:
/* A file to test imorting C modules for handling arrays to Python */
#include "Python.h"
#include "arrayobject.h"
#include "C_arraytest.h"
#include
/* #### Globals #################################### */
/* ==== Set up the methods table ====================== */
static PyMethodDef _C_arraytestMethods[] = {
{"vecfcn1", vecfcn1, METH_VARARGS},
{"vecsq", vecsq, METH_VARARGS},
{"rowx2", rowx2, METH_VARARGS},
{"rowx2_v2", rowx2_v2, METH_VARARGS},
{"matsq", matsq, METH_VARARGS},
{"contigmat", contigmat, METH_VARARGS},
{"intfcn1", intfcn1, METH_VARARGS},
{NULL, NULL} /* Sentinel - marks the end of this structure */
};
/* ==== Initialize the C_test functions ====================== */
// Module name must be _C_arraytest in compile and linked
void init_C_arraytest() {
(void) Py_InitModule("_C_arraytest", _C_arraytestMethods);
import_array(); // Must be present for NumPy. Called first after above line.
}
/* #### Vector Extensions ############################## */
/* ==== vector function - manipulate vector in place ======================
Multiply the input by 2 x dfac and put in output
Interface: vecfcn1(vec1, vec2, str1, d1)
vec1, vec2 are NumPy vectors,
str1 is Python string, d1 is Python float (double)
Returns integer 1 if successful */
static PyObject *vecfcn1(PyObject *self, PyObject *args)
{
PyArrayObject *vecin, *vecout; // The python objects to be extracted from the args
double *cin, *cout; // The C vectors to be created to point to the
// python vectors, cin and cout point to the row
// of vecin and vecout, respectively
int i,j,n;
const char *str;
double dfac;
/* Parse tuples separately since args will differ between C fcns */
if (!PyArg_ParseTuple(args, "O!O!sd", &PyArray_Type, &vecin,
&PyArray_Type, &vecout, &str, &dfac)) return NULL;
if (NULL == vecin) return NULL;
if (NULL == vecout) return NULL;
// Print out input string
printf("Input string: %s\n", str);
/* Check that objects are 'double' type and vectors
Not needed if python wrapper function checks before call to this routine */
if (not_doublevector(vecin)) return NULL;
if (not_doublevector(vecout)) return NULL;
/* Change contiguous arrays into C * arrays */
cin=pyvector_to_Carrayptrs(vecin);
cout=pyvector_to_Carrayptrs(vecout);
/* Get vector dimension. */
n=vecin->dimensions[0];
/* Operate on the vectors */
for ( i=0; i<n; i++)="" {="" cout[i]="2.0*dfac*cin[i];" }="" return="" py_buildvalue("i",="" 1);="" *="===" square="" vector="" components="" &="" multiply="" by="" a="" float="========================" returns="" new="" numpy="" array="" interface:="" vecsq(vec1,="" x1)="" vec1="" is="" vector,="" x1="" python="" (double)="" static="" pyobject="" *vecsq(pyobject="" *self,="" *args)="" pyarrayobject="" *vecin,="" *vecout;="" double="" *cin,="" *cout,="" dfactor;="" the="" c="" vectors="" to="" be="" created="" point="" vectors,="" cin="" and="" cout="" row="" of="" vecin="" vecout,="" respectively="" int="" i,j,n,m,="" dims[2];="" parse="" tuples="" separately="" since="" args="" will="" differ="" between="" fcns="" if="" (!pyarg_parsetuple(args,="" "o!d",="" &pyarray_type,="" &vecin,="" &dfactor))="" null;="" (null="=" vecin)="" check="" that="" object="" input="" 'double'="" type="" not="" needed="" wrapper="" function="" checks="" before="" call="" this="" routine="" (not_doublevector(vecin))="" get="" dimension="" n="dims[0]=vecin-">dimensions[0];
/* Make a new double vector of same dimension */
vecout=(PyArrayObject *) PyArray_FromDims(1,dims,NPY_DOUBLE);
/* Change contiguous arrays into C *arrays */
cin=pyvector_to_Carrayptrs(vecin);
cout=pyvector_to_Carrayptrs(vecout);
/* Do the calculation. */
for ( i=0; i<n; i++)="" {="" cout[i]="dfactor*cin[i]*cin[i];" }="" return="" pyarray_return(vecout);="" *="" ####="" vector="" utility="" functions="" #########################="" make="" a="" python="" array="" obj.="" from="" pyobject,="===============" generates="" double="" w="" contiguous="" memory="" which="" may="" be="" new="" allocation="" if="" the="" original="" was="" not="" type="" or="" !!="" must="" decref="" object="" returned="" this="" routine="" unless="" it="" is="" to="" caller="" of="" routines="" using="" pyarray_return(obj)="" pyarray_buildvalue="" with="" "n"="" construct="" !!!="" pyarrayobject="" *pyvector(pyobject="" *objin)="" (pyarrayobject="" *)="" pyarray_contiguousfromobject(objin,="" npy_double,="" 1,1);="" create="" 1d="" carray="" pyarray="=====================" assumes="" in="" memory.="" *pyvector_to_carrayptrs(pyarrayobject="" *arrayin)="" int="" i,n;="" n="arrayin-">dimensions[0];
return (double *) arrayin->data; /* pointer to arrayin data as double */
}
/* ==== Check that PyArrayObject is a double (Float) type and a vector ==============
return 1 if an error and raise exception */
int not_doublevector(PyArrayObject *vec) {
if (vec->descr->type_num != NPY_DOUBLE || vec->nd != 1) {
PyErr_SetString(PyExc_ValueError,
"In not_doublevector: array must be of type Float and 1 dimensional (n).");
return 1; }
return 0;
}
/* #### Matrix Extensions ############################## */
/* ==== Row x 2 function - manipulate matrix in place ======================
Multiply the 2nd row of the input by 2 and put in output
interface: rowx2(mat1, mat2)
mat1 and mat2 are NumPy matrices
Returns integer 1 if successful */
static PyObject *rowx2(PyObject *self, PyObject *args)
{
PyArrayObject *matin, *matout; // The python objects to be extracted from the args
double **cin, **cout; // The C matrices to be created to point to the
// python matrices, cin and cout point to the rows
// of matin and matout, respectively
int i,j,n,m;
/* Parse tuples separately since args will differ between C fcns */
if (!PyArg_ParseTuple(args, "O!O!", &PyArray_Type, &matin,
&PyArray_Type, &matout)) return NULL;
if (NULL == matin) return NULL;
if (NULL == matout) return NULL;
/* Check that objects are 'double' type and matrices
Not needed if python wrapper function checks before call to this routine */
if (not_doublematrix(matin)) return NULL;
if (not_doublematrix(matout)) return NULL;
/* Change contiguous arrays into C ** arrays (Memory is Allocated!) */
cin=pymatrix_to_Carrayptrs(matin);
cout=pymatrix_to_Carrayptrs(matout);
/* Get matrix dimensions. */
n=matin->dimensions[0];
m=matin->dimensions[1];
/* Operate on the matrices */
for ( i=0; i<n; 1="" 2="" i++)="" {="" for="" (="" j="0;" j<m;="" j++)="" if="" (i="=1)" cout[i][j]="2.0*cin[i][j];" }="" *="" free="" memory,="" close="" file="" and="" return="" free_carrayptrs(cin);="" free_carrayptrs(cout);="" py_buildvalue("i",="" 1);="" row="" x="" function-="" version="" 2.="" -="" manipulate="" matrix="" in="" place="=====================" multiply="" the="" 2nd="" of="" input="" by="" put="" output="" interface:="" rowx2(mat1,="" mat2)="" mat1="" mat2="" are="" numpy="" matrices="" returns="" integer="" successful="" uses="" utility="" function="" pymatrix="" to="" make="" c="" objects="" from="" pyobjects="" static="" pyobject="" *rowx2_v2(pyobject="" *self,="" *args)="" *pymatin,="" *pymatout;="" python="" be="" extracted="" args="" pyarrayobject="" *matin,="" *matout;="" array="" double="" **cin,="" **cout;="" created="" point="" matrices,="" cin="" cout="" rows="" matin="" matout,="" respectively="" int="" i,j,n,m;="" parse="" tuples="" separately="" since="" will="" differ="" between="" fcns="" (!pyarg_parsetuple(args,="" "oo",="" &pymatin,="" &pymatout))="" null;="" (null="=" pymatin)="" pymatout)="" convert="" matout="pymatrix(Pymatout);" check="" that="" 'double'="" type="" not="" needed="" wrapper="" checks="" before="" call="" this="" routine="" (not_doublematrix(matin))="" (not_doublematrix(matout))="" change="" contiguous="" arrays="" into="" **="" (memory="" is="" allocated!)="" get="" dimensions.="" n="matin-">dimensions[0];
m=matin->dimensions[1];
/* Operate on the matrices */
for ( i=0; i<n; i++)="" {="" for="" (="" j="0;" j<m;="" j++)="" if="" (i="=1)" cout[i][j]="2.0*cin[i][j];" }="" *="" free="" memory,="" close="" file="" and="" return="" free_carrayptrs(cin);="" free_carrayptrs(cout);="" py_buildvalue("i",="" 1);="" square="" matrix="" components="" function="" &="" multiply="" by="" int="" float="========" returns="" a="" new="" numpy="" array="" interface:="" matsq(mat1,="" i1,="" d1)="" mat1="" is="" matrix,="" i1="" python="" integer,="" d1="" (double)="" static="" pyobject="" *matsq(pyobject="" *self,="" *args)="" pyarrayobject="" *matin,="" *matout;="" double="" **cin,="" **cout,="" dfactor;="" i,j,n,m,="" dims[2],="" ifactor;="" parse="" tuples="" separately="" since="" args="" will="" differ="" between="" c="" fcns="" (!pyarg_parsetuple(args,="" "o!id",="" &pyarray_type,="" &matin,="" &ifactor,="" &dfactor))="" null;="" (null="=" matin)="" check="" that="" object="" input="" 'double'="" type="" not="" needed="" wrapper="" checks="" before="" call="" to="" this="" routine="" (not_doublematrix(matin))="" get="" the="" dimensions="" of="" n="dims[0]=matin-">dimensions[0];
m=dims[1]=matin->dimensions[1];
/* Make a new double matrix of same dims */
matout=(PyArrayObject *) PyArray_FromDims(2,dims,NPY_DOUBLE);
/* Change contiguous arrays into C ** arrays (Memory is Allocated!) */
cin=pymatrix_to_Carrayptrs(matin);
cout=pymatrix_to_Carrayptrs(matout);
/* Do the calculation. */
for ( i=0; i<n; i++)="" {="" for="" (="" j="0;" j<m;="" j++)="" cout[i][j]="ifactor*dfactor*cin[i][j]*cin[i][j];" }="" *="" free="" memory,="" close="" file="" and="" return="" free_carrayptrs(cin);="" free_carrayptrs(cout);="" pyarray_return(matout);="" operate="" on="" matrix="" components="" as="" contiguous="" memory="========================" shows="" how="" to="" access="" the="" array="" data="" a="" block="" of="" memory.="" used,="" example,="" in="" classes="" implemented="" contiquous="" rather="" than="" n="" arrays="" pointers="" "rows"="" returns="" new="" numpy="" interface:="" contigmat(mat1,="" x1)="" mat1="" is="" matrix,="" x1="" python="" float="" (double)="" static="" pyobject="" *contigmat(pyobject="" *self,="" *args)="" pyarrayobject="" *matin,="" *matout;="" double="" *cin,="" *cout,="" x1;="" matrices="" be="" used="" by="" c="" (e.g.="" passed="" program="" that="" uses="" int="" i,j,n,m,="" dims[2],="" ncomps;="" ncomps="n*m=total" number="" parse="" tuples="" separately="" since="" args="" will="" differ="" between="" fcns="" if="" (!pyarg_parsetuple(args,="" "o!d",="" &pyarray_type,="" &matin,="" &x1))="" null;="" (null="=" matin)="" check="" object="" input="" 'double'="" type="" not="" needed="" wrapper="" function="" checks="" before="" call="" this="" routine="" (not_doublematrix(matin))="" get="" dimensions="">dimensions[0];
m=dims[1]=matin->dimensions[1];
ncomps=n*m;
/* Make a new double matrix of same dims */
matout=(PyArrayObject *) PyArray_FromDims(2,dims,NPY_DOUBLE);
/* Change contiguous arrays into C * arrays pointers to PyArrayObject data */
cin=pyvector_to_Carrayptrs(matin);
cout=pyvector_to_Carrayptrs(matout);
/* Do the calculation. */
printf("In contigmat, cout (as contiguous memory) =\n");
for ( i=0; i<ncomps; i++)="" {="" cout[i]="cin[i]-x1;" printf("%e="" ",cout[i]);="" }="" printf("\n");="" return="" pyarray_return(matout);="" *="" ####="" matrix="" utility="" functions="" #########################="" make="" a="" python="" array="" obj.="" from="" pyobject,="===============" generates="" double="" w="" contiguous="" memory="" which="" may="" be="" new="" allocation="" if="" the="" original="" was="" not="" type="" or="" !!="" must="" decref="" object="" returned="" this="" routine="" unless="" it="" is="" to="" caller="" of="" routines="" using="" pyarray_return(obj)="" pyarray_buildvalue="" with="" "n"="" construct="" !!!="" pyarrayobject="" *pymatrix(pyobject="" *objin)="" (pyarrayobject="" *)="" pyarray_contiguousfromobject(objin,="" npy_double,="" 2,2);="" create="" carray="" pyarray="=====================" assumes="" in="" memory.="" allocated!="" **pymatrix_to_carrayptrs(pyarrayobject="" *arrayin)="" **c,="" *a;="" int="" i,n,m;="" n="arrayin-">dimensions[0];
m=arrayin->dimensions[1];
c=ptrvector(n);
a=(double *) arrayin->data; /* pointer to arrayin data as double */
for ( i=0; i<n; 1="" i++)="" {="" c[i]="a+i*m;" }="" return="" c;="" *="===" allocate="" a="" double="" *vector="" (vec="" of="" pointers)="=====================" memory="" is="" allocated!="" see="" void="" free_carray(double="" **="" )="" **ptrvector(long="" n)="" **v;="" v="(double" **)malloc((size_t)="" (n*sizeof(double)));="" if="" (!v)="" printf("in="" **ptrvector.="" allocation="" for="" array="" failed.");="" exit(0);="" v;="" free="" free_carrayptrs(double="" **v)="" free((char*)="" v);="" check="" that="" pyarrayobject="" (float)="" type="" and="" matrix="=============" an="" error="" raise="" exception="" int="" not_doublematrix(pyarrayobject="" *mat)="" (mat-="">descr->type_num != NPY_DOUBLE || mat->nd != 2) {
PyErr_SetString(PyExc_ValueError,
"In not_doublematrix: array must be of type Float and 2 dimensional (n x m).");
return 1; }
return 0;
}
/* #### Integer 2D Array Extensions ############################## */
/* ==== Integer function - manipulate integer 2D array in place ======================
Replace >=0 integer with 1 and < 0 integer with 0 and put in output
interface: intfcn1(int1, afloat)
int1 is a NumPy integer 2D array, afloat is a Python float
Returns integer 1 if successful */
static PyObject *intfcn1(PyObject *self, PyObject *args)
{
PyArrayObject *intin, *intout; // The python objects to be extracted from the args
int **cin, **cout; // The C integer 2D arrays to be created to point to the
// python integer 2D arrays, cin and cout point to the rows
// of intin and intout, respectively
int i,j,n,m, dims[2];
double afloat;
/* Parse tuples separately since args will differ between C fcns */
if (!PyArg_ParseTuple(args, "O!d",
&PyArray_Type, &intin, &afloat)) return NULL;
if (NULL == intin) return NULL;
printf("In intfcn1, the input Python float = %e, a C double\n",afloat);
/* Check that object input is int type and a 2D array
Not needed if python wrapper function checks before call to this routine */
if (not_int2Darray(intin)) return NULL;
/* Get the dimensions of the input */
n=dims[0]=intin->dimensions[0];
m=dims[1]=intin->dimensions[1];
/* Make a new int array of same dims */
intout=(PyArrayObject *) PyArray_FromDims(2,dims,NPY_LONG);
/* Change contiguous arrays into C ** arrays (Memory is Allocated!) */
cin=pyint2Darray_to_Carrayptrs(intin);
cout=pyint2Darray_to_Carrayptrs(intout);
/* Do the calculation. */
for ( i=0; i<n; i++)="" {="" for="" (="" j="0;" j<m;="" j++)="" if="" (cin[i][j]="">= 0) {
cout[i][j]= 1; }
else {
cout[i][j]= 0; }
} }
printf("In intfcn1, the output array is,\n\n");
for ( i=0; i<n; i++)="" {="" for="" (="" j="0;" j<m;="" j++)="" printf("%d="" ",cout[i][j]);="" }="" printf("\n");="" *="" free="" memory,="" close="" file="" and="" return="" free_cint2darrayptrs(cin);="" free_cint2darrayptrs(cout);="" pyarray_return(intout);="" ####="" integer="" array="" utility="" functions="" #########################="" make="" a="" python="" int="" obj.="" from="" pyobject,="===============" generates="" 2d="" w="" contiguous="" memory="" which="" may="" be="" new="" allocation="" if="" the="" original="" was="" not="" an="" type="" or="" !!="" must="" decref="" object="" returned="" this="" routine="" unless="" it="" is="" to="" caller="" of="" routines="" using="" pyarray_return(obj)="" pyarray_buildvalue="" with="" "n"="" construct="" !!!="" pyarrayobject="" *pyint2darray(pyobject="" *objin)="" (pyarrayobject="" *)="" pyarray_contiguousfromobject(objin,="" npy_long,="" 2,2);="" create="" carray="" pyarray="=====================" assumes="" in="" memory.="" allocated!="" **pyint2darray_to_carrayptrs(pyarrayobject="" *arrayin)="" **c,="" *a;="" i,n,m;="" n="arrayin-">dimensions[0];
m=arrayin->dimensions[1];
c=ptrintvector(n);
a=(int *) arrayin->data; /* pointer to arrayin data as int */
for ( i=0; i<n; 1="" i++)="" {="" c[i]="a+i*m;" }="" return="" c;="" *="===" allocate="" a="" *int="" (vec="" of="" pointers)="=====================" memory="" is="" allocated!="" see="" void="" free_carray(int="" **="" )="" int="" **ptrintvector(long="" n)="" **v;="" v="(int" **)malloc((size_t)="" (n*sizeof(int)));="" if="" (!v)="" printf("in="" **ptrintvector.="" allocation="" for="" array="" failed.");="" exit(0);="" v;="" free="" an="" *vector="" free_cint2darrayptrs(int="" **v)="" free((char*)="" v);="" check="" that="" pyarrayobject="" (integer)="" type="" and="" 2d="" error="" raise="" exception="" note:="" use="" ny_long="" numpy="" integer="" array,="" not="" np_int="" not_int2darray(pyarrayobject="" *mat)="" (mat-="">descr->type_num != NPY_LONG || mat->nd != 2) {
PyErr_SetString(PyExc_ValueError,
"In not_int2Darray: array must be of type int and 2 dimensional (n x m).");
return 1; }
return 0;
}
// EOF</n;> </n;></n;></n;></ncomps;></n;></n;></n;></n;></n;>
现在,让我们看一下小块的源代码。
头球
您必须在 Python 中包含以下标题。h 总是包含第一个标题。
#include "Python.h"
#include "arrayobject.h"
我还包括标题 C_arraytest.h,它包含 matsq 函数的原型:
static PyObject *matsq(PyObject *self, PyObject *args);
函数声明前面的 static 关键字使这个函数成为扩展模块的私有函数。链接器就是看不到它。这样,您可以对所有扩展模块使用相同的直觉函数名(即 sum、check、trace),而不会在链接时发生名称冲突。该函数的类型是PyObject *
,因为它将总是返回一个 Python 调用函数,所以您可以(实际上必须)返回一个 Python 对象。争论总是一样的,
PyObject *self and PyObject *args
第一个自我从未被使用过,但由于 Python 传递参数的方式,它是必要的。第二个参数是指向 Python 元组的指针,该元组包含函数的所有参数(B,I,x)。
方法定义
这将建立一个函数名表,作为 Python 代码到 C 扩展的接口。C 扩展模块的名称将为_C_arraytest
(注意前导下划线)。每次使用名称时都要正确,这很重要,因为在代码中使用模块名称有严格的要求。该名称首先出现在方法定义表中,作为表名的第一部分:
static PyMethodDef _C_arraytestMethods[] = {
...,
{"matsq", matsq, METH_VARARGS},
...,
{NULL, NULL} /* Sentinel - marks the end of this structure */
};
我用省略号(...)忽略与此函数无关的其他代码。METH_VARARGS
参数告诉编译器,您将以通常的方式传递参数,而不使用关键字,如上面的示例A=matsq(B,i,x)
所示。Python 关键词有很多使用方法,但是我没有尝试过。表应该总是以{NULL,NULL}结尾,这只是一个“标记”,用来标记表的结尾。
初始化
这些函数告诉 Python 解释器在加载模块时调用什么。请注意,在初始化结构的名称中,模块的名称(_C_arraytest
)必须直接位于 init 之后。
void init_C_arraytest() {
(void) Py_InitModule("_C_arraytest", _C_arraytestMethods);
import_array(); // Must be present for NumPy. Called first after above line.
}
顺序很重要,必须先调用这两个初始化函数。
matsqfunction 代码
下面是您将从 Python 代码中调用的实际函数。我将把它分开,并解释每个部分。
函数名称和类型:
static PyObject *matsq(PyObject *self, PyObject *args)
你可以看到它们与 C_arraytest.h 中的原型相匹配
局部变量:
PyArrayObject *matin, *matout;
double **cin, **cout, dfactor;
int i,j,n,m, dims[2], ifactor;
PyArrayObjects
是 NumPy 头文件中定义的结构,它们将被分配指向实际输入和输出 NumPy 数组(A 和 B)的指针。C 数组cin
和cout
是指针,它们将(最终)指向 NumPy 数组中的实际数据,并允许您对其进行操作。变量dfactor
将是 Python 浮点 y,ifactor
将是 Python int i,变量 I,j,n 和 m 将是 A 和 b 中的循环变量(I 和 j)和矩阵维数(n=行数,m=列数)。数组 dims 将用于从 NumPy 数组访问 n 和 m。这一切都发生在下面。首先,我们必须从参数元组中提取输入变量(A,I,y)。这是通过调用完成的,
/* Parse tuples separately since args will differ between C fcns */
if (!PyArg_ParseTuple(args, "O!id",
&PyArray_Type, &matin, &ifactor, &dfactor)) return NULL;
PyArg_ParseTuple
函数接受参数元组,并使用下一个出现的格式字符串(“O!id”),它将元组的每个成员分配给一个 C 变量。注意你必须通过引用传递所有的 C 变量。即使 C 变量是一个指向字符串的指针,也是如此(参见 vecfcn1 例程中的代码)。格式字符串告诉解析函数使用什么类型的变量。Python 的公共变量都有字母名称(例如,s 代表字符串,I 代表整数,d 代表(Python 浮点数的两倍))。你可以在圭多的教程(http://docs.python.org/ext/ext.html)中找到这些以及更多的列表。对于不在标准 Python 中的数据类型,比如 NumPy 数组,您可以使用 O!告诉解析器寻找类型结构(在这种情况下是 NumPy 结构PyArray_Type
)的符号,以帮助它转换将被分配给指向 NumPy 数组结构的局部变量(matin)的元组成员。注意这些也是通过引用传递的。顺序必须保持,并与您想要的 Python 函数的调用接口相匹配。格式字符串定义了接口,如果您没有从 Python 中调用函数,因此参数的数量与格式字符串中的数量相匹配,您将会得到一个错误。这很好,因为它将指出问题所在。
如果这不起作用,我们返回空值,这将导致 Python 异常。
if (NULL == matin) return NULL;
接下来我们检查输入矩阵是否真的是 NumPy 类型的双精度矩阵。这个测试也是在这个 C 扩展的 Python 包装器中完成的。最好在那里做,但是我在这里包含测试,向您展示您可以在 C 扩展中做测试,并且您可以“接触”NumPy 结构来挑选它的参数。效用函数not_doublematrix
将在后面解释。
/* Check that object input is 'double' type and a matrix
Not needed if python wrapper function checks before call to this routine */
if (not_doublematrix(matin)) return NULL;
这里有一个例子,涉及到 NumPy 结构,以获得矩阵 matin 的维度,并将其分配给如上所述的局部变量。
/* Get the dimensions of the input */
n=dims[0]=matin->dimensions[0];
m=dims[1]=matin->dimensions[1];
现在我们使用这些矩阵参数在我们的 C 扩展中生成一个新的 NumPy 矩阵 matout(我们的输出)。PyArray_FromDims(2,Dims,NPY_DOUBLE)是一个由 NumPy(不是我)提供的实用函数,它的参数告诉 NumPy NumPy 对象的秩(2)、每个维度的大小(dims)和数据类型(NPY_DOUBLE)。创建不同 NumPy 数组的其他例子在其他 C 扩展中。
/* Make a new double matrix of same dims */
matout=(PyArrayObject *) PyArray_FromDims(2,dims,NPY_DOUBLE);
为了实际进行计算,我们需要 C 结构来处理数据,因此我们生成了两个 C 2 维数组(cin 和 cout),它们将分别指向 matin 和 matout 中的数据。注意,这里分配了内存,因为我们需要创建一个指向 C doubles 的指针数组,这样我们就可以像通常的 C 矩阵一样用两个索引来处理 cin 和 cout。这个内存必须在这个 C 扩展结束时释放。像这样的内存分配并不总是必要的。请参阅 C 扩展(例程 contigmat)中的 NumPy 向量操作和将 NumPy 矩阵视为连续数组(因为它们在 NumPy 中)的例程。
/* Change contiguous arrays into C ** arrays (Memory is Allocated!) */
cin=pymatrix_to_Carrayptrs(matin);
cout=pymatrix_to_Carrayptrs(matout);
最后,我们可以操作矩阵并进行计算。这里是进行原始方程运算\(B_{ij}= i y (A_{ij})^2\)
的部分。注意,我们直接操作传递给这个扩展的原始 NumPy 数组 A 和 B 中的数据。因此,您在这里对 cin 或 cout 的组件所做的任何操作都将对原始矩阵进行,并且当您返回 Python 代码时,这些操作将出现在那里。
/* Do the calculation. */
for ( i=0; i<n; i++) {
for ( j=0; j<m; j++) {
cout[i][j]= ifactor*dfactor*cin[i][j]*cin[i][j];
}
}
我们准备回到 Python 调用例程,但是首先我们释放分配给 cin 和 cout 的内存。
/* Free memory, close file and return */
free_Carrayptrs(cin);
free_Carrayptrs(cout);
现在我们返回计算结果。
return PyArray_Return(matout);
如果您查看其他 C 扩展,您可以看到您也可以使用另一个 Python 提供的函数Py_BuildValue("i", 1)
返回常规 Python 变量(如 ints),其中字符串“I”告诉函数数据类型,第二个参数(此处为 1)是要返回的数据值。如果您决定不返回任何内容,您必须像这样返回 Python 关键字 None:
Py_INCREF(Py_None);
return Py_None;
Py _ INCREF 函数增加了对 None 的引用数量(请记住,当不再有对数据的引用时,Python 会自动收集分配的内存)。在 C 扩展中,您必须小心这一点。更多信息见圭多教程。
实用功能
下面是一些矩阵实用函数的快速描述。它们几乎是不言自明的。向量和整数数组实用函数非常相似。
第一个实用函数没有在这里的任何 C 扩展中使用,但是我包含了它,因为一个有用的人把它和一些代码一起发送了,它确实展示了如何将 python 对象转换成 NumPy 数组。我没试过。使用自担风险。
PyArrayObject *pymatrix(PyObject *objin) {
return (PyArrayObject *) PyArray_ContiguousFromObject(objin,
NPY_DOUBLE, 2,2);
}
下一个创建 C 数组,用于指向 NumPy 矩阵的行。这将分配指向 NumPy 数据的指针数组。NumPy 数据是连续的,步长(m)用于访问每行。这个函数调用 ptserver ctor(n),它进行实际的内存分配。使用这个后记得释放内存。
double **pymatrix_to_Carrayptrs(PyArrayObject *arrayin) {
double **c, *a;
int i,n,m;
n=arrayin->dimensions[0];
m=arrayin->dimensions[1];
c=ptrvector(n);
a=(double *) arrayin->data; /* pointer to arrayin data as double */
for ( i=0; i<n; i++) {
c[i]=a+i*m;
}
return c;
}
这里是指针 C 数组的内存分配位置。这是一个非常标准的数组内存分配器。
double **ptrvector(long n) {
double **v;
v=(double **)malloc((size_t) (n*sizeof(double)));
if (!v) {
printf("In **ptrvector. Allocation of memory for double array failed.");
exit(0);
}
return v;
}
这是释放内存的例程。
void free_Carrayptrs(double **v) {
free((char*) v);
}
注:有一个标准的 C-API,用来从 Python 对象转换成 C 风格的指针数组,叫做 PyArray _ AsCArray
这里有一个实用函数,它检查以确保解析产生的对象是一个 NumPy 矩阵。你可以看到它是如何进入 NumPy 对象结构的。
int not_doublematrix(PyArrayObject *mat) {
if (mat->descr->type_num != NPY_DOUBLE || mat->nd != 2) {
PyErr_SetString(PyExc_ValueError,
"In not_doublematrix: array must be of type Float and 2 dimensional (n x m).");
return 1; }
return 0;
}
《民法典》——其他变体
正如我在介绍中提到的,这些函数是重复的。所有其他函数遵循非常相似的模式。它们在方法结构中有一行,它们有相同的参数,它们解析参数,它们可能在解析后检查 C 结构,它们设置 C 变量来操作哪个指向输入数据,它们进行实际计算,它们释放内存(如果需要),它们为 Python 返回一些东西(要么无,要么 Python 对象)。我将只提到上面矩阵 C 扩展 matsq 的代码中的一些差异。
vecfc 1:
解析函数的格式字符串指定 Python 中的变量是字符串。
if (!PyArg_ParseTuple(args, "O!O!sd", &PyArray_Type, &vecin,
&PyArray_Type, &vecout, &str, &dfac)) return NULL;
在局部 C 数组的指针赋值中没有分配内存,因为它们已经是向量了。
cin=pyvector_to_Carrayptrs(vecin);
cout=pyvector_to_Carrayptrs(vecout);
如果成功,则返回 int = 1。这是作为 Python int 返回的。
return Py_BuildValue("i", 1);
行 x2:
在这个例程中,我们通过参数元组列表中的引用传递输出,并通过操作在适当的位置进行更改,从而“传回”输出。将此与从 matsq 中的 C 扩展返回数组进行比较。任何一方都能完成任务。
和你在一起 mat:
在这里,矩阵数据被视为一个长向量(就像首尾相连地堆叠行一样)。如果 C++中有数组类,将数据存储为一个长向量,然后像数组一样使用跨步来访问它(二维、三维或其他),这将非常有用。即使 matin 和 matout 是“矩阵”,我们也像对待向量一样对待它们,并使用向量工具来获得我们的 C 指针 cin 和 cout。
/* Change contiguous arrays into C * arrays pointers to PyArrayObject data */
cin=pyvector_to_Carrayptrs(matin);
cout=pyvector_to_Carrayptrs(matout);
对于其他实用函数,请注意,我们使用不同的等级、维度和 NumPy 参数(例如NPY_LONG
)来告诉我们正在调用的例程数据类型是什么。
制作文件
制作文件非常简单。它是为 Mac OS X 10.4 编写的,称为 BSD Unix。
# ---- Link ---------------------------
_C_arraytest.so: C_arraytest.o C_arraytest.mak gcc -bundle -flat_namespace -undefined suppress -o _C_arraytest.so C_arraytest.o
# ---- gcc C compile ------------------
C_arraytest.o: C_arraytest.c C_arraytest.h C_arraytest.mak gcc -c C_arraytest.c
-I/Library/Frameworks/Python.framework/Versions/2.4/include/python2.4
-I/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/numpy/core/include/numpy/
编译步骤非常标准。您确实需要添加 Python 头的路径:
-I/Library/Frameworks/Python.framework/Versions/2.4/include/python2.4
和 NumPy 头的路径:
-I/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/numpy/core/include/numpy/
这些路径用于在苹果操作系统上安装框架 Python 2.4。您需要提供安装在计算机上的标题路径。他们可能不一样。我的猜测是 gcc 标志对于编译将是相同的。
链接步骤产生实际的模块(_C_arraytest.so
),可以导入 Python 代码。这是 Mac OS X 系统特有的。在 Linux 或 Windows 上,您将需要一些不同的东西。我一直在寻找一般性的例子,但我不确定我的发现对大多数人是否有用,所以我选择不在那里显示这些发现。我无法判断代码是否适合那些系统。
请注意,生成的共享库的名称''' '必须' ' '与 C 扩展源代码中的初始化和方法定义调用中的名称相匹配。因此名字中的前导下划线_C_arraytest.so
。
这是我修改过的 Makefile,它在 Linux 下编译这段代码(把它作为 Makefile 保存在同一个目录下,然后运行‘make’
保罗·伊万诺夫
# ---- Link ---------------------------
_C_arraytest.so: C_arraytest.o
gcc -shared C_arraytest.o -o _C_arraytest.so
# ---- gcc C compile ------------------
C_arraytest.o: C_arraytest.c C_arraytest.h
gcc -c C_arraytest.c -I/usr/include/python2.4 -I/usr/lib/python2.4/site-packages/numpy/core/include/numpy
Python 包装代码
这里就像在 C 代码中一样,我将只显示一个包装函数及其使用的详细描述。重复太多了,如果你理解一个,其他包装器就会很清楚。我将再次使用 matsq 函数。这是在导入包装器模块(C_arraytest.py
)后,当您在自己的 Python 代码中调用 matsq 函数时将首先调用的代码,该包装器模块会自动导入并使用_C_arraytest.so
中的实际 C 扩展(以对用户隐藏的方式)(注意将名称分开的前导下划线)。
进口:
导入 C 扩展、NumPy 和系统模块(用于结尾的 exit 语句,这是可选的)。
import _C_arraytest
import numpy as NP
import sys
Python matsq 函数的定义
传递一个 NumPy 矩阵(matin)、一个 Python int (ifac)和一个 Python float (dfac)。检查参数,确保它们是正确的类型、尺寸和大小。这在 Python 方面更加容易和安全,这就是为什么我在这里这样做,尽管我在 C 扩展中展示了一种方法来做到这一点。
def matsq(matin, ifac, dfac):
# .... Check arguments, double NumPy matrices?
test=NP.zeros((2,2)) # create a NumPy matrix as a test object to check matin
typetest= type(test) # get its type
datatest=test.dtype # get data type
if type(matin) != typetest:
raise 'In matsq, matrix argument is not *NumPy* array'
if len(NP.shape(matin)) != 2:
raise 'In matsq, matrix argument is not NumPy *matrix*'
if matin.dtype != datatest:
raise 'In matsq, matrix argument is not *Float* NumPy matrix'
if type(ifac) != type(1):
raise 'In matsq, ifac argument is not an int'
if type(dfac) != type(1.0):
raise 'In matsq, dfac argument is not a python float'
最后,调用 C 扩展在 matin 上进行实际计算。
# .... Call C extension function
return _C_arraytest.matsq(matin, ifac, dfac)
你可以看到 python 部分是最简单的。
使用你的 C 分机
如果测试函数mattest2
在另一个模块中(您正在编写的模块),下面是如何在脚本中使用它来调用包装的 matsq 函数。
from C_arraytest import * # Note, NO underscore since you are importing the wrapper.
import numpy as NP
import sys
def mattest2():
print "\n--- Test matsq ------------------------------"
print " Each 2nd matrix component should = square of 1st matrix component x ifac x dfac \n"
n=4 # Number of columns # Make 2 x n matrices
z=NP.arange(float(n))
x=NP.array([z,3.0*z])
jfac=2
xfac=1.5
y=matsq(x, jfac, xfac)
print "x=",x
print "y=",y
if __name__ == '__main__':
mattest2()
输出如下所示:
--- Test matsq ------------------------------
Each 2nd matrix component should = square of 1st matrix component x ifac x dfac
x= [[ 0\. 1\. 2\. 3.]
[ 0\. 3\. 6\. 9.]]
y= [[ 0\. 3\. 12\. 27.]
[ 0\. 27\. 108\. 243.]]
所有测试功能的输出都在文件C_arraytest
输出中。
摘要
这是解释我写的 C 扩展的初稿(有帮助)。如果你对代码、错误等有意见。请将它们发布在 pythonmac 电子邮件列表中。我会看到他们的。
我编写了 C 扩展来使用 NumPy,尽管它们最初是为 Numeric 编写的。如果你必须使用数字,你应该测试它们是否兼容。我怀疑像NPY_DOUBLE
这样的名字,比如不会是。我强烈建议你升级到 NumPy,因为它是 Python 中数值的未来。值得付出努力。
评论
请注意,这一行虽然在上面的头文件中,但在。沥青球里的 h。
static PyObject *rowx2_v2(PyObject *self, PyObject *args);
保罗·伊万诺夫
对于调试版本,输出文件名应为 _C_arraytest_d.pyd,对于发布版本,输出文件名应为 _C_arraytest.pyd。
杰弗里·朱
ptrvector()分配n*sizeof(double)
,但应该真的分配指针到 double 所以:n*sizeof(double *)
-彼得·梅尔沃德
在 vecsq()中,第 86 行,因为您将创建一个一维向量:
int i,j,n,m, dims[2];
应该是:
int i,j,n,m, dims[1];
在pyvector_to_Carrayptrs()
中,从不使用n
。
–弗雷德里克森
附件
在图形用户界面中嵌入特征
在图形用户界面中嵌入特征
在特性应用中嵌入 Matplotlib 图形
特性是智能工具套装的一部分,它为创建图形用户界面应用提供了一个很好的框架,而不需要很多将用户界面与应用逻辑的其余部分连接起来的普通样板。性状的简单介绍可以在这里找到。虽然 ETS 自带了自己的特征感知绘图框架(查科),但如果你已经知道 matplotlib,那么嵌入这个框架也同样容易。Chaco (IMHO)的优势在于其交互式“工具”、一个(正在开发中的)OpenGL 渲染后端和一个易于理解的代码库。然而,matplotlib 有更多更好的文档和更好的默认值;它就是有效。让 TraitsUI 和 matplotlib 发挥出色的关键是使用 mpl 面向对象的 API,而不是 pylab / pyplot。该秘籍需要以下包装:
- numpy
- wxPython
- matplotlib
- 性状> 3.0
- TraitsGUI > 3.0
- TraitsBackendWX > 3.0
在本例中,我们将显示一个函数(y,正弦波),该函数由一个变量(x,numpy ndarray)和一个参数(scale,一个有边界的浮点值)组成。我们希望能够改变用户界面中的参数,并在绘图窗口中看到 y 的变化。最终结果是这样的:火车!“CustomEditor”可用于显示任何 wxPython 窗口作为对象的编辑器。你只要通过了!CustomEditor 是一个可调用的,当被调用时,返回您想要显示的 wxPython 窗口。这种情况下,我们的!函数的作用是:返回一个包含 mpl 的 wxPanel!图形画布和导航工具栏。这个例子利用了一些特征。我们使用“动态初始化”按需创建轴和线 2D 对象(使用 _xxx_default 方法)。我们用性状“通知”,来调用 update_line(...)每当 x 或 y 数据改变时。此外,y 数据被声明为属性特征,该属性特征取决于“比例”参数和 x 数据无论何时“比例”或“x”发生变化,“y”都会根据需要重新计算。如果 y 的从属关系*
未被*
修改,则“缓存属性”装饰器会阻止 y 的重新计算
最后,在 redraw()方法中有一点 wx-magic,通过将实际绘制延迟 50 毫秒来限制重绘速率。这使用了 wx。!调用 class 类。这可以防止拖动滑块时过度重绘,防止用户界面滞后。 Here's
完整上市:
#!python
"""
A simple demonstration of embedding a matplotlib plot window in
a traits-application. The CustomEditor allow any wxPython window
to be used as an editor. The demo also illustrates Property traits,
which provide nice dependency-handling and dynamic initialisation, using
the _xxx_default(...) method.
"""
from enthought.traits.api import HasTraits, Instance, Range,\
Array, on_trait_change, Property,\
cached_property, Bool
from enthought.traits.ui.api import View, Item
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure
from matplotlib.axes import Axes
from matplotlib.lines import Line2D
from enthought.traits.ui.api import CustomEditor
import wx
import numpy
def MakePlot(parent, editor):
"""
Builds the Canvas window for displaying the mpl-figure
"""
fig = editor.object.figure
panel = wx.Panel(parent, -1)
canvas = FigureCanvasWxAgg(panel, -1, fig)
toolbar = NavigationToolbar2Wx(canvas)
toolbar.Realize()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(canvas,1,wx.EXPAND|wx.ALL,1)
sizer.Add(toolbar,0,wx.EXPAND|wx.ALL,1)
panel.SetSizer(sizer)
return panel
class PlotModel(HasTraits):
"""A Model for displaying a matplotlib figure"""
#we need instances of a Figure, a Axes and a Line2D
figure = Instance(Figure, ())
axes = Instance(Axes)
line = Instance(Line2D)
_draw_pending = Bool(False) #a flag to throttle the redraw rate
#a variable paremeter
scale = Range(0.1,10.0)
#an independent variable
x = Array(value=numpy.linspace(-5,5,512))
#a dependent variable
y = Property(Array, depends_on=['scale','x'])
traits_view = View(
Item('figure',
editor=CustomEditor(MakePlot),
resizable=True),
Item('scale'),
resizable=True
)
def _axes_default(self):
return self.figure.add_subplot(111)
def _line_default(self):
return self.axes.plot(self.x, self.y)[0]
@cached_property
def _get_y(self):
return numpy.sin(self.scale * self.x)
@on_trait_change("x, y")
def update_line(self, obj, name, val):
attr = {'x': "set_xdata", 'y': "set_ydata"}[name]
getattr(self.line, attr)(val)
self.redraw()
def redraw(self):
if self._draw_pending:
return
canvas = self.figure.canvas
if canvas is None:
return
def _draw():
canvas.draw()
self._draw_pending = False
wx.CallLater(50, _draw).Start()
self._draw_pending = True
if __name__=="__main__":
model = PlotModel(scale=2.0)
model.configure_traits()
附件
Matplotlib:拖放文本示例
Matplotlib:拖放文本示例
介绍
Matplotlib 提供事件处理来确定诸如按键、鼠标位置和按钮点击之类的事情。Matplotlib 支持许多图形用户界面,并通过 mpl_connect 和 mpl_disconnect 方法为图形用户界面事件处理提供了一个接口。
该页面通过为文本对象添加拖放处理程序给出了使用这些工具的示例。这里可以得到这个例子的源代码: Text_DragnDrop_v0.1.py
。)
定义处理程序类
#!python numbers=disable
from matplotlib import pylab as p
from matplotlib.text import Text
class DragHandler(object):
""" A simple class to handle Drag n Drop.
This is a simple example, which works for Text objects only
"""
def __init__(self, figure=None) :
""" Create a new drag handler and connect it to the figure's event system.
If the figure handler is not given, the current figure is used instead
"""
if figure is None : figure = p.gcf()
# simple attibute to store the dragged text object
self.dragged = None
# Connect events and callbacks
figure.canvas.mpl_connect("pick_event", self.on_pick_event)
figure.canvas.mpl_connect("button_release_event", self.on_release_event)
def on_pick_event(self, event):
" Store which text object was picked and were the pick event occurs."
if isinstance(event.artist, Text):
self.dragged = event.artist
self.pick_pos = (event.mouseevent.xdata, event.mouseevent.ydata)
return True
def on_release_event(self, event):
" Update text position and redraw"
if self.dragged is not None :
old_pos = self.dragged.get_position()
new_pos = (old_pos[0] + event.xdata - self.pick_pos[0],
old_pos[1] + event.ydata - self.pick_pos[1])
self.dragged.set_position(new_pos)
self.dragged = None
p.draw()
return True
一个小用例
#! python numbers=disable
# Usage example
from numpy import *
# Create arbitrary points and labels
x, y = random.normal(5, 2, size=(2, 9))
labels = [ "Point %d" % i for i in xrange(x.size)]
# trace a scatter plot
p.scatter(x, y)
p.grid()
# add labels and set their picker attribute to True
for a,b,l in zip(x,y, labels):
p.text(a, b, l, picker=True)
# Create the event hendler
dragh = DragHandler()
p.show()
现在可以用鼠标移动文本对象。
附件
Matplotlib: treemap
Matplotlib: treemap
树图是一种显示树信息的好方法,它不是基于连接节点的方法。
参见http://www.cs.umd.edu/hcil/treemap/
[]文件/附件/matplotlib _ tree map/tree map . png
"""
Treemap builder using pylab.
Uses algorithm straight from http://hcil.cs.umd.edu/trs/91-03/91-03.html
James Casbon 29/7/2006
"""
import pylab
from matplotlib.patches import Rectangle
class Treemap:
def __init__(self, tree, iter_method, size_method, color_method):
"""create a tree map from tree, using itermethod(node) to walk tree,
size_method(node) to get object size and color_method(node) to get its
color"""
self.ax = pylab.subplot(111,aspect='equal')
pylab.subplots_adjust(left=0, right=1, top=1, bottom=0)
self.ax.set_xticks([])
self.ax.set_yticks([])
self.size_method = size_method
self.iter_method = iter_method
self.color_method = color_method
self.addnode(tree)
def addnode(self, node, lower=[0,0], upper=[1,1], axis=0):
axis = axis % 2
self.draw_rectangle(lower, upper, node)
width = upper[axis] - lower[axis]
try:
for child in self.iter_method(node):
upper[axis] = lower[axis] + (width * float(size(child))) / size(node)
self.addnode(child, list(lower), list(upper), axis + 1)
lower[axis] = upper[axis]
except TypeError:
pass
def draw_rectangle(self, lower, upper, node):
print lower, upper
r = Rectangle( lower, upper[0]-lower[0], upper[1] - lower[1],
edgecolor='k',
facecolor= self.color_method(node))
self.ax.add_patch(r)
if __name__ == '__main__':
# example using nested lists, iter to walk and random colors
size_cache = {}
def size(thing):
if isinstance(thing, int):
return thing
if thing in size_cache:
return size_cache[thing]
else:
size_cache[thing] = reduce(int.__add__, [size(x) for x in thing])
return size_cache[thing]
import random
def random_color(thing):
return (random.random(),random.random(),random.random())
tree= ((5,(3,5)), 4, (5,2,(2,3,(3,2,2)),(3,3)), (3,2) )
Treemap(tree, iter, size, random_color)
pylab.show()
附件
Mayavi:从源代码安装 python
Mayavi:从源代码安装 python
继http://www.enthought.com/enthought/wiki/GrabbingAndBuilding之后,你必须从源代码中构建/安装 VTK 5.0 和一些 python 扩展。
给定 python 模块或 VTK 所需的所有安装信息都可以在其网页上找到。
对于不耐烦的人,这些信息在这里恢复。
关于配置脚本的注意事项:如果您没有指定软件包的安装目标,它们将通过/usr/local 中的 defaut 进行安装。
我们在这里选择将它们安装在个人目录中,比如~/Mayavi2。所以我们将环境变量 DESTDIR 设置为~/Mayavi2,后面会称之为 DESTDIR:
在 sh 壳状下,键入:
export DESTDIR=~/Mayavi2
在 csh 壳状下,键入:
setenv DESTDIR ~/Mayavi2
Is 还假设您在一个名为 src/的特定目录中下载并解压缩所有 tarball 源。
安装 python2.3/python2.4
在http://www.python.org/download/releases/2.3.5下载 Python-2.3.5.tar.bz2,或者在http://www.python.org/download/releases/2.4.3下载 Python-2.4.3.tar.bz2,并在 src/:
cd src && tar xvfj Python-2.4.3.tar.bz2
然后运行:
cd Python-2.4.3/ && ./configure --enable-shared --enable-unicode=ucs4 --prefix=$DESTDIR
然后,您可以制作并安装:
make && make install
安装 VTK 5.0
在http://public.kitware.com/VTK/get-software.php下载 vtk-5.0.0.tar.gz 和 vtkdata-5.0.0.tar.gz,并在 src/取消下载:
cd src/ && tar xvfz vtk-5.0.0.tar.gz && tar xvfz vtkdata-5.0.0.tar.gz
注意:继续之前必须安装 cmake 包。
运行:
cd VTK && ccmake .
创建所需的 Makefile。
按“c”进行配置。
然后按下所选项目上的“回车”来切换标志。
如果 ccmake 没有找到它们,您应该指定一些信息,特别是关于一些库的位置(tcl/tk libs + dev 包和刚刚安装的 python2.3/python2.4)和目的地(将其设置为 DESTDIR)。
不要忘记将标志“VTK_WRAP_PYTHON”设置为开(如果您想使用 Tcl/Tk,则设置为“VTK_WRAP_TCL”):
BUILD_EXAMPLES ON
BUILD_SHARED_LIBS ON
CMAKE_BACKWARDS_COMPATIBILITY 2.0
CMAKE_BUILD_TYPE
CMAKE_INSTALL_PREFIX DESTDIR
VTK_DATA_ROOT DESTDIR/VTKData
VTK_USE_PARALLEL OFF
VTK_USE_RENDERING ON
VTK_WRAP_JAVA OFF
VTK_WRAP_PYTHON ON
VTK_WRAP_TCL ON
按“c”继续配置:
PYTHON_INCLUDE_PATH *DESTDIR/include/python2.4
PYTHON_LIBRARY *DESTDIR/lib/libpython2.4.so
TCL_INCLUDE_PATH */usr/include/tcl8.4
TCL_LIBRARY */usr/lib/libtcl8.4.so
TK_INCLUDE_PATH */usr/include/tcl8.4
TK_LIBRARY */usr/lib/libtk8.4.so
VTK_USE_RPATH *OFF
BUILD_EXAMPLES ON
BUILD_SHARED_LIBS ON
CMAKE_BACKWARDS_COMPATIBILITY 2.0
CMAKE_BUILD_TYPE
CMAKE_INSTALL_PREFIX DESTDIR
VTK_DATA_ROOT DESTDIR/VTKData
VTK_USE_PARALLEL OFF
VTK_USE_RENDERING ON
VTK_WRAP_JAVA OFF
VTK_WRAP_PYTHON ON
VTK_WRAP_TCL ON
注意:您可以按“t”获取更多配置选项。
按“c”然后按“g”退出配置,然后键入:
make && make install
安装 wx-Python2.6
在 https://sourceforge.net/project/showfiles.php?下载 wxPython-src-2.6.3.2.tar.gzgroup_id=10718 并在 src/中取消绑定:
cd src/ && tar xvfz wxPython-src-2.6.3.2.tar.gz
注意:您应该安装 GTK 2,也就是说,您应该安装 libgtk-2.6。and
libgtk 2.6 .-安装了开发包。
然后运行:
cd wxPython-src-2.6.3.2/ && ./configure --enable-unicode --with-opengl --prefix=$DESTDIR
然后你可以做:
make; make -C contrib/src/animate; make -C contrib/src/gizmos; make -C contrib/src/stc
或者按照 wx-Python2.6 网页上的说明,创建一个小脚本,自动运行上面的命令。
然后安装所有:
make install; make -C contrib/src/animate install ; make -C contrib/src/gizmos install; make -C contrib/src/stc install
要构建 python 模块:
cd wxPython
然后运行:
./setup.py build_ext --inplace --debug UNICODE=1
并安装它们:
./setup.py install UNICODE=1 --prefix=$DESTDIR
安装 scipy 0.5 和 numpy 1.0
在http://www.scipy.org/Download下载 scipy-0.5.1.tar.gz
在安装 scipy 之前,您必须下载并安装:
* numpy-1.0.tar.gz (http://sourceforge.net/project/showfiles.php?group_id=1369&package_id=175103)
* Atlas libraries (you could install it with your packages manager, no need to build it in src/)
安装这些 python 扩展不需要特殊选项。
要在我们的$DESTDIR 中安装这些软件包,只需更改目录并键入:
./setup.py install --prefix=$DESTDIR
就这样,伙计们!
安装前!MayaVi2,你要设置一些环境变量,才能讲!可以找到 python 扩展的 MayaVi2。
在 sh 壳状下,键入:
export PYTHONPATH=$DESTDIR:$PYTHONPATH
export LD_LIBRARY_PATH=$DESTDIR:$LD_LIBRARY_PATH
在 csh 壳状下,键入:
setenv PYTHONPATH ${DESTDIR}:${PYTHONPATH}
setenv LD_LIBRARY_PATH ${DESTDIR}:${LD_LIBRARY_PATH}
玛雅维:例子
玛雅维:例子
|| 本页展示了使用高级、面向对象的 API 编写 Mayavi2 脚本。Mayavi2 最近获得了一个易于使用的脚本模块:mlab,虽然它的功能可能不那么强大。请参考美亚威 2 用户指南的部分。||
介绍
在这里,你将看到一些你可以使用的渲染场景的例子!MayaVi2。建议您阅读[:Cookbook/MayaVi/scriptingmayavi 2]以了解您看到的内容,尽管这里给出的大多数示例都是不言自明的。
|| 请注意,这些例子不是最新的。最新版本的 Mayavi 的示例库可以在上找到。||
使用等值面模块的示例
#!/usr/bin/env mayavi2
"""This script demonstrates how one can script MayaVi and use its
contour related modules. Notice the magic line at the top.
"""
# Author: Prabhu Ramachandran <prabhu_r@users.sf.net>
# Copyright (c) 2005-2007, Enthought, Inc.
# License: BSD Style.
# Standard library imports
from os.path import join, dirname
# Enthought library imports
import enthought.mayavi
from enthought.mayavi.sources.vtk_file_reader import VTKFileReader
from enthought.mayavi.filters.threshold import Threshold
from enthought.mayavi.modules.outline import Outline
from enthought.mayavi.modules.grid_plane import GridPlane
from enthought.mayavi.modules.contour_grid_plane import ContourGridPlane
from enthought.mayavi.modules.iso_surface import IsoSurface
from enthought.mayavi.modules.scalar_cut_plane import ScalarCutPlane
def contour():
"""The script itself. We needn't have defined a function but
having a function makes this more reusable.
"""
# 'mayavi' is always defined on the interpreter.
# Create a new scene.
mayavi.new_scene()
# Read a VTK (old style) data file.
r = VTKFileReader()
r.initialize(join(dirname(enthought.mayavi.__file__),
'examples', 'data', 'heart.vtk'))
mayavi.add_source(r)
# Create an outline for the data.
o = Outline()
mayavi.add_module(o)
# Create three simple grid plane modules.
# First normal to 'x' axis.
gp = GridPlane()
mayavi.add_module(gp)
# Second normal to 'y' axis.
gp = GridPlane()
mayavi.add_module(gp)
gp.grid_plane.axis = 'y'
# Third normal to 'z' axis.
gp = GridPlane()
mayavi.add_module(gp)
gp.grid_plane.axis = 'z'
# Create one ContourGridPlane normal to the 'x' axis.
cgp = ContourGridPlane()
mayavi.add_module(cgp)
# Set the position to the middle of the data.
cgp.grid_plane.position = 15
# Another with filled contours normal to 'y' axis.
cgp = ContourGridPlane()
mayavi.add_module(cgp)
# Set the axis and position to the middle of the data.
cgp.grid_plane.axis = 'y'
cgp.grid_plane.position = 15
cgp.contour.filled_contours = True
# An isosurface module.
iso = IsoSurface(compute_normals=True)
mayavi.add_module(iso)
iso.contour.contours = [220.0]
# An interactive scalar cut plane.
cp = ScalarCutPlane()
mayavi.add_module(cp)
cp.implicit_plane.normal = 0,0,1
if __name__ == '__main__':
contour()
[](文件/附件/MayaVi_examples/contour.png
使用字形模块的示例
#!/usr/bin/env mayavi2
"""This script demonstrates the use of a VectorCutPlane, splitting the
pipeline using a MaskPoints filter and then viewing the filtered data
with the Glyph module.
"""
# Author: Prabhu Ramachandran <prabhu_r@users.sf.net>
# Copyright (c) 2005-2007, Enthought, Inc.
# License: BSD Style.
# Standard library imports
from os.path import join, dirname
# Enthought library imports
import enthought.mayavi
from enthought.mayavi.sources.vtk_xml_file_reader import VTKXMLFileReader
from enthought.mayavi.modules.outline import Outline
from enthought.mayavi.modules.glyph import Glyph
from enthought.mayavi.modules.vector_cut_plane import VectorCutPlane
from enthought.mayavi.modules.vectors import Vectors
from enthought.mayavi.filters.mask_points import MaskPoints
def glyph():
"""The script itself. We needn't have defined a function but
having a function makes this more reusable.
"""
# 'mayavi' is always defined on the interpreter.
# Create a new VTK scene.
mayavi.new_scene()
# Read a VTK (old style) data file.
r = VTKXMLFileReader()
r.initialize(join(dirname(enthought.mayavi.__file__),
'examples', 'data', 'fire_ug.vtu'))
mayavi.add_source(r)
# Create an outline and a vector cut plane.
mayavi.add_module(Outline())
v = VectorCutPlane()
mayavi.add_module(v)
v.glyph.color_mode = 'color_by_scalar'
# Now mask the points and show glyphs (we could also use
# Vectors but glyphs are a bit more generic)
m = MaskPoints()
m.filter.set(on_ratio=10, random_mode=True)
mayavi.add_filter(m)
g = Glyph()
mayavi.add_module(g)
# Note that this adds the module to the filtered output.
g.glyph.scale_mode = 'scale_by_vector'
# Use arrows to view the scalars.
g.glyph.glyph_source = g.glyph.glyph_list[1]
if __name__ == '__main__':
glyph()
[](文件/附件/MayaVi_examples/glyph.png
不带 Mayavi 2 UI(nonui . py)的示例
#!/usr/bin/env python
"""This script demonstrates how one can use the MayaVi framework
without displaying MayaVi's UI. Note: look at the end of this file to
see how the non gui plugin is chosen instead of the default gui
mayavi plugin.
"""
# Author: Prabhu Ramachandran <prabhu_r@users.sf.net>
# Copyright (c) 2005, Enthought, Inc.
# License: BSD Style.
# On systems with multiple wx installations installed, pick one that works
# with the libraries Mayavi depends on.
try:
import wxversion
wxversion.ensureMinimal('2.6')
except ImportError:
pass
# Standard library imports
import sys
from os.path import join, dirname
# Enthought library imports
from enthought.mayavi.app import Mayavi, NONGUI_PLUGIN_DEFINITIONS
class MyApp(Mayavi):
def run(self):
"""This is executed once the application GUI has started.
*Make sure all other MayaVi specific imports are made here!*
"""
# Various imports to do different things.
from enthought.mayavi.sources.vtk_file_reader import VTKFileReader
from enthought.mayavi.modules.outline import Outline
from enthought.mayavi.modules.axes import Axes
from enthought.mayavi.modules.grid_plane import GridPlane
from enthought.mayavi.modules.image_plane_widget import ImagePlaneWidget
from enthought.mayavi.modules.text import Text
from enthought.mayavi.modules.contour_grid_plane import ContourGridPlane
from enthought.mayavi.modules.iso_surface import IsoSurface
script = self.script
# Create a new scene.
script.new_scene()
# Read a VTK (old style) data file.
r = VTKFileReader()
r.initialize('data/heart.vtk')
r.initialize(join(dirname(__file__), 'data', 'heart.vtk'))
script.add_source(r)
# Put up some text.
t = Text(text='MayaVi rules!', x_position=0.2, y_position=0.9, width=0.8)
t.property.color = 1, 1, 0 # Bright yellow, yeah!
script.add_module(t)
# Create an outline for the data.
o = Outline()
script.add_module(o)
# Create an axes for the data.
a = Axes()
script.add_module(a)
# Create three simple grid plane modules.
# First normal to 'x' axis.
gp = GridPlane()
script.add_module(gp)
# Second normal to 'y' axis.
gp = GridPlane()
gp.grid_plane.axis = 'y'
script.add_module(gp)
# Third normal to 'z' axis.
gp = GridPlane()
script.add_module(gp)
gp.grid_plane.axis = 'z'
# Create one ImagePlaneWidget.
ipw = ImagePlaneWidget()
script.add_module(ipw)
# Set the position to the middle of the data.
ipw.ipw.slice_position = 16
# Create one ContourGridPlane normal to the 'x' axis.
cgp = ContourGridPlane()
script.add_module(cgp)
# Set the position to the middle of the data.
cgp.grid_plane.axis = 'y'
cgp.grid_plane.position = 15
# An isosurface module.
iso = IsoSurface(compute_normals=True)
script.add_module(iso)
iso.contour.contours = [200.0]
# Set the view.
s = script.engine.current_scene
cam = s.scene.camera
cam.azimuth(45)
cam.elevation(15)
s.render()
if __name__ == '__main__':
m = MyApp()
# Note how we change the plugins that are loaded only here.
m.main(plugin_defs=NONGUI_PLUGIN_DEFINITIONS)
[](文件/附件/Mayavi _ examples/non ui . png
以三维数组作为数值源的示例
#!/usr/bin/env mayavi2
"""This script demonstrates how to create a numpy array data and
visualize it as image data using a few modules.
"""
# Author: Prabhu Ramachandran <prabhu_r@users.sf.net>
# Copyright (c) 2005-2007, Enthought, Inc.
# License: BSD Style.
# Standard library imports
import enthought.util.scipyx as scipy
# Enthought library imports
from enthought.mayavi.sources.array_source import ArraySource
from enthought.mayavi.modules.outline import Outline
from enthought.mayavi.modules.image_plane_widget import ImagePlaneWidget
def make_data(dims=(128, 128, 128)):
"""Creates some simple array data of the given dimensions to test
with."""
np = dims[0]*dims[1]*dims[2]
# Create some scalars to render.
x, y, z = scipy.ogrid[-5:5:dims[0]*1j,-5:5:dims[1]*1j,-5:5:dims[2]*1j]
x = x.astype('f')
y = y.astype('f')
z = z.astype('f')
scalars = (scipy.sin(x*y*z)/(x*y*z))
return scipy.transpose(scalars).copy() # This makes the data contiguous.
def view_numpy():
"""Example showing how to view a 3D numpy array in mayavi2.
"""
# 'mayavi' is always defined on the interpreter.
mayavi.new_scene()
# Make the data and add it to the pipeline.
data = make_data()
src = ArraySource(transpose_input_array=False)
src.scalar_data = data
mayavi.add_source(src)
# Visualize the data.
o = Outline()
mayavi.add_module(o)
ipw = ImagePlaneWidget()
mayavi.add_module(ipw)
ipw.module_manager.scalar_lut_manager.show_scalar_bar = True
ipw_y = ImagePlaneWidget()
mayavi.add_module(ipw_y)
ipw_y.ipw.plane_orientation = 'y_axes'
if __name__ == '__main__':
view_numpy()
[](文件/附件/Mayavi _ examples/numeric _ source . png
使用流线模块的示例
#!/usr/bin/env mayavi2
"""This script demonstrates how one can script MayaVi to display
streamlines and an iso surface.
"""
# Author: Prabhu Ramachandran <prabhu_r@users.sf.net>
# Copyright (c) 2005-2007, Enthought, Inc.
# License: BSD Style.
# Standard library imports
from os.path import join, dirname
# Enthought library imports
from enthought.mayavi.sources.vtk_xml_file_reader import VTKXMLFileReader
from enthought.mayavi.modules.outline import Outline
from enthought.mayavi.modules.streamline import Streamline
from enthought.mayavi.modules.iso_surface import IsoSurface
def setup_data(fname):
"""Given a VTK XML file name `fname`, this creates a mayavi2
reader for it and adds it to the pipeline. It returns the reader
created.
"""
mayavi.new_scene()
r = VTKXMLFileReader()
r.initialize(fname)
mayavi.add_source(r)
return r
def streamline():
"""Sets up the mayavi pipeline for the visualization.
"""
# Create an outline for the data.
o = Outline()
mayavi.add_module(o)
s = Streamline(streamline_type='tube')
mayavi.add_module(s)
s.stream_tracer.integration_direction = 'both'
s.seed.widget.center = 3.5, 0.625, 1.25
s.module_manager.scalar_lut_manager.show_scalar_bar = True
i = IsoSurface()
mayavi.add_module(i)
i.contour.contours[0] = 550
i.actor.property.opacity = 0.5
if __name__ == '__main__':
import enthought.mayavi
fname = join(dirname(enthought.mayavi.__file__),
'examples', 'data', 'fire_ug.vtu')
r = setup_data(fname)
streamline()
[](文件/附件/Mayavi _ examples/streamline . png
使用 ImagePlaneWidget 模块的示例
#!/usr/bin/env python
"""This script demonstrates how one can script MayaVi, set its size,
create a new VTK scene and create a few simple modules.
"""
# Author: Prabhu Ramachandran <prabhu_r@users.sf.net>
# Copyright (c) 2005, Enthought, Inc.
# License: BSD Style.
# On systems with multiple wx installations installed, pick one that works
# with the libraries Mayavi depends on.
try:
import wxversion
wxversion.ensureMinimal('2.6')
except ImportError:
pass
# Standard library imports
import sys
from os.path import join, dirname
# Enthought library imports
from enthought.mayavi.app import Mayavi
class MyApp(Mayavi):
def run(self):
"""This is executed once the application GUI has started.
*Make sure all other MayaVi specific imports are made here!*
"""
# Various imports to do different things.
from enthought.mayavi.sources.vtk_file_reader import VTKFileReader
from enthought.mayavi.filters.threshold import Threshold
from enthought.mayavi.modules.outline import Outline
from enthought.mayavi.modules.axes import Axes
from enthought.mayavi.modules.grid_plane import GridPlane
from enthought.mayavi.modules.image_plane_widget import ImagePlaneWidget
from enthought.mayavi.modules.text import Text
script = self.script
# Create a new scene.
script.new_scene()
# Read a VTK (old style) data file.
r = VTKFileReader()
r.initialize(join(dirname(__file__), 'data', 'heart.vtk'))
script.add_source(r)
# Put up some text.
t = Text(text='MayaVi rules!', x_position=0.2,
y_position=0.9, width=0.8)
t.property.color = 1, 1, 0 # Bright yellow, yeah!
script.add_module(t)
# Create an outline for the data.
o = Outline()
script.add_module(o)
# Create an axes for the data.
a = Axes()
script.add_module(a)
# Create an orientation axes for the scene. This only works with
# VTK-4.5 and above which is why we have the try block.
try:
from enthought.mayavi.modules.orientation_axes import OrientationAxes
except ImportError:
pass
else:
a = OrientationAxes()
a.marker.set_viewport(0.0, 0.8, 0.2, 1.0)
script.add_module(a)
# Create three simple grid plane modules.
# First normal to 'x' axis.
gp = GridPlane()
script.add_module(gp)
# Second normal to 'y' axis.
gp = GridPlane()
gp.grid_plane.axis = 'y'
script.add_module(gp)
# Third normal to 'z' axis.
gp = GridPlane()
script.add_module(gp)
gp.grid_plane.axis = 'z'
# Create one ImagePlaneWidget.
ipw = ImagePlaneWidget()
script.add_module(ipw)
# Set the position to the middle of the data.
ipw.ipw.slice_position = 16
if __name__ == '__main__':
a = MyApp()
a.main()
[](文件/附件/MayaVi_examples/test.png
使用 mlab (surf_regular_mlab.py)的示例
另请参阅[:Cookbook/MayaVi/Surf]了解另一种方法。
#!/usr/bin/env mayavi2
"""Shows how to view data created by `enthought.tvtk.tools.mlab` with
mayavi2.
"""
# Author: Prabhu Ramachandran <prabhu@aero.iitb.ac.in>
# Copyright (c) 2006-2007, Enthought Inc.
# License: BSD Style.
import numpy
from enthought.tvtk.tools import mlab
from enthought.mayavi.sources.vtk_data_source import VTKDataSource
from enthought.mayavi.filters.warp_scalar import WarpScalar
from enthought.mayavi.modules.outline import Outline
from enthought.mayavi.modules.surface import Surface
def f(x, y):
"""Some test function.
"""
return numpy.sin(x*y)/(x*y)
def make_data():
"""Make some test numpy data and create a TVTK data object from it
that we will visualize.
"""
x = numpy.arange(-7., 7.05, 0.1)
y = numpy.arange(-5., 5.05, 0.05)
s = mlab.SurfRegular(x, y, f)
return s.data
def add_data(tvtk_data):
"""Add a TVTK data object `tvtk_data` to the mayavi pipleine.
"""
d = VTKDataSource()
d.data = tvtk_data
mayavi.add_source(d)
def surf_regular():
"""Now visualize the data as done in mlab.
"""
w = WarpScalar()
mayavi.add_filter(w)
o = Outline()
s = Surface()
mayavi.add_module(o)
mayavi.add_module(s)
if __name__ == '__main__':
mayavi.new_scene()
d = make_data()
add_data(d)
surf_regular()
附件
使用 Pyparsing 读取自定义文本文件
使用 Pyparsing 读取自定义文本文件
介绍
在这本秘籍中,我们将重点使用 pyparsing 和 numpy 来阅读像这样的结构化文本文件, data.txt
:
# This is is an example file structured in section
# with comments begining with '#'
[ INFOS ]
Debug = False
Shape (mm^-1) = 2.3 # here is a unit
Length (mm) = 25361.15
Path 1 = C:\\This\is\a\long\path\with some space in it\data.txt
description = raw values can have multiple lines, but additional lines must start
with a whitespace which is automatically skipped
Parent = None
[ EMPTY SECTION ]
# empty section should not be taken into account
[ TABLE IN ROWS ]
Temp (C) 100 200 300 450.0 600
E XX (GPa) 159.4 16.9E+0 51.8 .15E02 4 # Here is a space in the row name
Words 'hundred' 'two hundreds' 'a lot' 'four' 'five' # Here are QuotedStrings with space
[ TABLE IN COLUMNS ]
STATION PRECIPITATION T_MAX_ABS T_MIN_ABS
(/) (mm) (C) (C) # Columns must have a unit
Ajaccio 64.8 18.8E+0 -2.6
Auxerre 49.6 16.9E+0 Nan # Here is a Nan
Bastia 114.2 20.8E+0 -0.9
[ MATRIX ]
True 2 3
4\. 5\. 6.
7\. nan 8
我们将创建一个可重用的解析器类来自动:
* detect section blocs, among four possible kinds :`` * a set of variable declarations :
name
(
unit
) =
value
,
unit
is optional`` * a table defined row by row, where the first column defines the name of the row. This name can have spaces in it if it is followed by an unit, otherwise it can't.`` * a table defined column by column. Column names can't contain spaces and the second row should in this case contains units`` * a matrix containing only numeric values, True, False or NaN``* convert values into the adequate Python or Numpy type (True, False, None, NaN, float, str or array)``* detect associated units if present``* return a data structure with the same organization in section as the input file and clean up variable name to get a name compatible with named attribute access
下面是这个解析器的会话示例, ConfigNumParser
: )#
>>> from ConfigNumParser import *
>>> data = parseConfigFile('data.txt')
>>> pprint(data.asList())
[['infos',
['debug', False],
['shape', 2.2999999999999998],
['length', 25361.150000000001],
['path_1', 'C:\\\\This\\is\\a\\long\\path\\with some space in it\\data.txt'],
['description',
'raw values can have multiple lines, but additional lines must start\nwith a whitespace which is automatically skipped'],
['parent', None],
['names_', ['debug', 'shape', 'length', 'path_1', 'description', 'parent']],
['unit_', {'length': 'mm', 'shape': 'mm^-1'}]],
['table_in_rows',
['temp', array([ 100., 200., 300., 450., 600.])],
['e_xx', array([ 159.4, 16.9, 51.8, 15\. , 4\. ])],
['words', array(['hundred', 'two hundreds', 'a lot', 'four', 'five'], dtype='|S12')],
['names_', ['temp', 'e_xx', 'words']],
['unit_', {'e_xx': 'GPa', 'temp': 'C'}]],
['table_in_columns',
['station', array(['Ajaccio', 'Auxerre', 'Bastia'], dtype='|S7')],
['precipitation', array([ 64.8, 49.6, 114.2])],
['t_max_abs', array([ 18.8, 16.9, 20.8])],
['t_min_abs', array([-2.6, NaN, -0.9])],
['names_', ['station', 'precipitation', 't_max_abs', 't_min_abs']],
['unit_', {'precipitation': 'mm', 't_max_abs': 'C', 't_min_abs': 'C'}]],
['matrix',
array([[ 1., 2., 3.],
[ 4., 5., 6.],
[ 7., NaN, 8.]])]]
>>> data.matrix
array([[ 1., 2., 3.],
[ 4., 5., 6.],
[ 7., NaN, 8.]])
>>> data.table_in_columns.t_max_abs
array([ 18.8, 16.9, 20.8])
>>> data.infos.length, data.infos.unit_['length']
(25361.15, 'mm')
这个解析器在除矩阵字段之外的所有部分中添加了两个特殊字段:
*
“名称” _
: a list containing the names of all variables found in this section*
unit_
`:包含每个变量名对应的单位的字典,如果有的话
为参数声明定义解析器
pyparsing 是处理格式化文本的高效工具,让你分两步处理:
1\. Define rules to identify strings representing sections, variable names, and so on. With pyparsing, theses rules can be combined easily with the standard operators | and + and creating reusable components becomes easy too.
1\. Define actions to be executed on theses fields, to convert them into python objects.
在上面的文件示例中,有四种数据:参数定义、行中的表、列中的表和矩阵。
因此,我们将为每个解析器定义一个解析器,并将它们组合起来定义最终的解析器。
py 解析的第一步
本节将逐步描述如何构建在 ConfigNumParser
中定义的函数paramParser
,用于解析)#上例中的 bloc【INFOS】。
参数声明的形式如下:
key
(
unit
) =
value
with:`` *
key
: a set of alphanumeric characters or _`` *
unit
: an optional set of alphanumeric characters or ^ * / - . _`` *
value
: anything to the end of line or to the character # which starts a comment
这几乎可以用 pyparsing 语法逐字翻译(更多信息参见如何使用 pyparsing ):
from pyparsing import *
# parameter definition
keyName = Word(alphanums + '_')
unitDef = '(' + Word(alphanums + '^*/-._') + ')'
paramValueDef = SkipTo('#'|lineEnd)
paramDef = keyName + Optional(unitDef) + "=" + empty + paramValueDef
很容易测试在数据文件中使用这种模式会发现什么:
# print all params found
>>> for param in paramDef.searchString(file('data.txt').read()):
... print param.dump()
... print '...'
['Context', '=', 'full']
...
['Temp_ref', '(', 'K', ')', '=', '298.15']
...
...
我们可以在几个方面进行改进:
element, ``* give a name to the different fields, with the
抑制element, ``* give a name to the different fields, with the
七个结果名称method, or simply just by calling an element with the name in argument
# parameter definition
keyName = Word(alphanums + '_')
unitDef = Suppress('(') + Word(alphanums + '^*/-._') + Suppress(')')
paramValueDef = SkipTo('#'|lineEnd)
paramDef = keyName('name') + Optional(unitDef)('unit') + Suppress("="+empty) + paramValueDef('value')
现在,测试将为结果命名,并给出更好的输出:
['Context', 'full']
- name: Context
- value: full
...
['Temp_ref', 'K', '298.15']
- name: Temp_ref
- unit: ['K']
- value: 298.15
...
...
将数据转换为 Python 对象
我们将进一步详细说明什么样的值可以让 pyparsing 处理转换。
它们可以分为两部分:
* Python objects like numbers, True, False, None, NaN or any string between quotes.``* Raw strings that should not be converted
让我们从数字开始。我们可以使用Regex
元素快速检测代表数字的字符串:
from re import VERBOSE
number = Regex(r"""
[+-]? # optional sign
(
(?:\d+(?P<float1>\.\d*)?) # match 2 or 2.02
| # or
(?P<float2>\.\d+) # match .02
)
(?P<float3>[Ee][+-]?\d+)? # optional exponent
""", flags=VERBOSE
)
有关正则表达式的更多信息,请参见正则表达式操作。我们可以用标准的 pyparsing 元素(Combine
、Optional
、oneOf
等)构建一个解析器。)但是像浮点数这样的低级表达式据说使用Regex
类会做得更好。我知道这感觉像作弊,但事实上,pyparsing 在封面下使用了许多 re。
现在,我们将定义一个函数,将该字符串转换为 python float 或 integer,并设置一个parseAction
来告诉 pyparsing 在找到一个数字时自动转换该数字:
def convertNumber(t):
"""Convert a string matching a number to a python number"""
if t.float1 or t.float2 or t.float3 : return [float(t[0])]
else : return [int(t[0]) ]
number.setParseAction(convertNumber)
convertNumber
功能是parseAction
的一个简单例子:
* it should accepts a
解析结果 object as input value (some functions can accepts 3 parameters, see
设置解析操作 documentation). A
解析结果 object can be used as a list, as a dict or directly with a named attribute if you have named your results. Here we had set three named group float1, float2 and float3 and we can use them to decide whether to use int() or float().
* it should return either a
解析结果object or a list of results which will be automatically converted to a
解析结果object.
Pyparsing 附带了一个非常方便的功能,可以将字段转换为常量对象,即replaceWith
。这可用于创建将字符串转换为 python 对象的元素列表:
from numpy import NAN
pyValue_list = [ number ,
Keyword('True').setParseAction(replaceWith(True)) ,
Keyword('False').setParseAction(replaceWith(False)) ,
Keyword('NAN', caseless=True).setParseAction(replaceWith(NAN)),
Keyword('None').setParseAction(replaceWith(None)) ,
QuotedString('"""', multiline=True) ,
QuotedString("'''", multiline=True) ,
QuotedString('"') ,
QuotedString("'") ,
]
pyValue = MatchFirst( e.setWhitespaceChars(' \t\r') for e in pyValue_list)
这里我们使用了:
*
关键词to detect standard python keyword and replace them on the fly ``*
上市串to detect quoted string and automatically unquote them``*
配对第to build a super element,
pyValue to convert all kind of python values.
让我们看看我们得到了什么:
>>> test2 = '''
>>> 1 2 3.0 0.3 .3 2e2 -.2e+2 +2.2256E-2
>>> True False nan NAN None
>>> "word" "two words"
>>> """'more words', he said"""
>>> '''
>>> print pyValue.searchString(test2)
[[1], [2], [3.0], [0.29999999999999999], [0.29999999999999999], [200.0], [-20.0], [0.022256000000000001],
[True], [False], [nan], [nan], [None], ['word'], ['two words'], ["'more words', he said"]]
一些关于空白字符的单词
默认情况下,pyparsing 将' \t\r\n '中的任何字符视为空白且无意义。如果您需要检测线端,您需要使用setWhitespaceChars
或setDefaultWhitespaceChars
来更改此行为。
因为我们要一行一行地处理表格,我们需要配置这个,这个应该在最底层设置:
>>> pyValue2 = MatchFirst(pyValue_list) # default behavior
>>> print OneOrMore(pyValue2).searchString(test2)
[[1, 2, 3.0, 0.29999999999999999, 0.29999999999999999, 200.0, -20.0, 0.022256000000000001, True, False, nan, nan, None, 'word', 'two words', "'more words', he said"]]
>>> # to compare to
>>> for r, s, t in OneOrMore(pyValue).searchString(test2)
[[1, 2, 3.0, 0.29999999999999999, 0.29999999999999999, 200.0, -20.0, 0.022256000000000001],
[True, False, nan, nan, None],
['word', 'two words'],
["'more words', he said"]]
转换变量名称
我们还必须详细说明什么是可接受的参数名称。
由于参数名的末尾由=字符分隔,我们可以接受其中包含空格。但是,由于我们希望有可能通过一个命名属性来访问它的值,我们需要将它转换成一个标准的形式,与 python 的命名约定兼容。在这里,我们选择将参数名称格式化为小写,并在“-/.”中包含任意一组字符替换为下划线。
稍后,我们将不得不处理不允许有空格的参数名。所以我们必须定义两种名称:
def variableParser(escapedChars, baseChars=alphanums):
""" Return pattern matching any characters in baseChars separated by
characters defined in escapedChars. Thoses characters are replaced with '_'
The '_' character is therefore automatically in escapedChars.
"""
escapeDef = Word(escapedChars + '_').setParseAction(replaceWith('_'))
whitespaceChars = ''.join( x for x in ' \t\r' if not x in escapedChars )
escapeDef = escapeDef.setWhitespaceChars(whitespaceChars)
return Combine(Word(baseChars) + Optional(OneOrMore(escapeDef + Word(baseChars))))
keyName = variableParser(' _-./').setParseAction(downcaseTokens)
keyNameWithoutSpace = variableParser('_-./').setParseAction(downcaseTokens)
downcaseTokens
是一个特殊的 pyparsing 函数,返回每个匹配的小写标记。
处理原始文本
为了完成这个解析器,我们现在需要添加一个规则来匹配以下条件下的原始文本:
* anything after the # character is considered as a comment and skipped``* a raw value can be on several lines, but the additional lines must start with a whitespace and not with a [
# rawValue can be multiline but theses lines should start with a Whitespace
rawLine = CharsNotIn('#\n') + (lineEnd | Suppress('#'+restOfLine))
rawValue = Combine( rawLine + ZeroOrMore(White(' \t').suppress()+ NotAny('[') + rawLine))
rawValue.setParseAction(lambda t: [x.strip() for x in t])
我们还将细化单元的定义,以处理特殊情况,如(-)、(/)或(),对应于空白单元。
这导致:
unitDef = Suppress('(') + (Suppress(oneOf('- /')) | Optional(Word(alphanums + '^*/-._'))) + Suppress(')')
valueDef = pyValue | rawValue
paramDef = keyName('name') + Optional(unitDef)('unit') + Suppress("="+empty) + valueDef('value')
结构化数据
我们将尝试以易于使用的数据结构来组织结果。
为此,我们将使用Dict
元素,它允许像普通字典一样通过键或命名属性进行访问。这个元素接受找到的每个标记,它的第一个字段作为键名,后面的字段作为值。当您可以使用Group
元素将数据分组为只有两个字段时,这非常方便。
因为我们可以有三个(带单位),我们将把这些单位放在一边:
def formatBloc(t):
""" Format the result to have a list of (key, values) easily usable with Dict
Add two fields :
names_ : the list of column names found
units_ : a dict in the form {key : unit}
"""
rows = []
# store units and names
units = {}
names = []
for row in t :
rows.append(ParseResults([ row.name, row.value ]))
names.append(row.name)
if row.unit : units[row.name] = row.unit[0]
rows.append( ParseResults([ 'names_', names ]))
rows.append( ParseResults([ 'unit_', units]))
return rows
paramParser = Dict( OneOrMore( Group(paramDef)).setParseAction(formatBloc))
这个paramParser
元素正是在 ConfigNumParser
中定义的函数paramParser
创建的解析器。
让我们看看我们得到了什么:
>>> paramParser.ignore('#' + restOfLine)
>>> data = paramParser.searchString(file('data.txt').read())[0]
>>> print data.dump()
[...]
- debug: False
- description: raw values can have multiple lines, but additional lines must start
with a whitespace which is automatically skipped
- length: 25361.15
- names_: ['debug', 'shape', 'length', 'path_1', 'description', 'parent']
- parent: None
- path_1: 'C:\\This\is\a\long\path\with some space in it\data.txt'
- shape: 2.3
- unit_: {'shape': 'mm^-1', 'length': 'mm'}
>>> data.length, data.unit_['length']
Out[12]: (25361.150000000001, 'mm')
为表定义解析器
对于解析参数声明,我们已经看到了大多数常见的技术,但只有一种:使用Forward
元素动态定义解析规则。
让我们看看如何根据这个模式,用它来一列一列地解析一个表:
Name_1 Name_2 ... Name_n
(unit_1) (unit_2) ... (unit_n)
value_11 value_21 ... value_n1
... ... ... ...
以及以下规则:
* Names can't contains any whitespaces.``* Units are mandatory.``* Value can be any standard python value (int, number, None, False, True, NaN or quoted strings) or a raw string which can't contains spaces or '['.
这样的解析器可以用 ConfigNumParser
中定义的tableColParser
函数生成。
问题的核心是告诉 pyparsing 每行应该有相同数量的列,而这个数量是未知的。
使用前进元素
我们将在读取标题行之后,通过定义对应于单元行及其追随者的模式来解决这个问题。
事实上,这些行可以用一个Forward
元素来定义,我们可以在标题行上附加一个parseAction
来重新定义这些元素,一旦我们知道标题中有多少列。
通过< <操作符重新定义Forward
元素:
# We define ends-of-line and what kind of values we expect in tables
EOL = LineEnd().suppress()
tabValueDef = pyValue | CharsNotIn('[ \t\r\n').setWhitespaceChars(" \t")
# We define how to detect the first line, which is a header line
# following lines will be defined later
firstLine = Group(OneOrMore(keyNameWithoutSpace)+EOL)
unitLine = Forward()
tabValueLine = Forward()
def defineColNumber(t):
""" Define unitLine and tabValueLine to match the same number of columns than
the header line"""
nbcols = len(t.header)
unitLine << Group( unitDef*nbcols + EOL)
tabValueLine << Group( tabValueDef*nbcols + EOL)
tableColDef = ( firstLine('header').setParseAction(defineColNumber)
+ unitLine('unit')
+ Group(OneOrMore(tabValueLine))('data')
)
构建我们的数据
现在,我们将像组织参数一样组织数据,但这次我们将使用列名作为关键字,并将数据转换为 numpy 数组:
def formatBloc(t):
""" Format the result to have a list of (key, values) easily usable
with Dict and transform data into array
Add two fields :
names_ : the list of column names found
units_ : a dict in the form {key : unit}
"""
columns = []
# store names and units names
names = t.header
units = {}
transposedData = zip(*t.data)
for header, unit, data in zip(t.header, t.unit, transposedData):
units[header] = unit
columns.append(ParseResults([header, array(data)]))
columns.append(ParseResults(['names_', names]))
columns.append(ParseResults(['unit_' , units ]))
return columns
tableColParser = Dict(tableColDef.setParseAction(formatBloc))
让我们看看我们得到了什么:
>>> tableColParser.ignore('#' + restOfLine)
>>> data = tableColParser.searchString(file('data3.txt').read())[0]
>>> print data.dump()
[...]
- names_: ['station', 'precipitation', 't_max_abs', 't_min_abs']
- precipitation: [ 64.8 49.6 114.2]
- station: ['Ajaccio' 'Auxerre' 'Bastia']
- t_max_abs: [ 18.8 16.9 20.8]
- t_min_abs: [-2.6 NaN -0.9]
- unit_: {'station': '/', 'precipitation': 'mm', 't_min_abs': 'C', 't_max_abs': 'C'}
构建最终的解析器
我们现在有三种解析器:
*
变量解析器:handle variables names``*
参数解析器:handle a set of variable definitions``*
数组解析器:handle tables defined column by column
ConfigNumParser
中还有两个:
*
表解析器handle tables defined row by row``*
矩阵解析器handle matrix containg only python values or NaN
我们不会在这里详述它们,因为它们使用了我们已经看到的完全相同的技术。
我们更愿意看到如何将它们组合成一个复杂的解析器,就像在parseConfigFile
函数中所做的那样:
# Section header
sectionName = Suppress('[') + keyName + Suppress(']')
# Group section name and content
section = Group (sectionName +
( paramParser()
| tableColParser()
| tableRowParser()
| matrixParser()
) )
# Build the final parser and suppress empty sections
parser = Dict( OneOrMore( section | Suppress(sectionName) ))
# Defines comments
parser.ignore('#' + restOfLine)
仅此而已。
解析器现在可以通过其方法parseString
或parseFile
使用。有关更多信息,请参见[附件:ConfigNumParser _ v 0 . 1 . 1 . py ConfigNumParser]。
我希望这能给你一个阅读复杂格式文本的良好起点。
附件
脚本 Mayavi 2:基本模块
脚本 Mayavi 2:基本模块
介绍
这些模块被称为“基本”模块,因为它们是通用的,并且独立于所有类型的数据。
在使用模块之前,提醒您必须导入它。
通常,如果要更改对象的颜色,您必须键入:
module.actor.property.color = fg_color
其中 fg_color 是一个“元组”,比如黑色的(0,0,0)。
注意:如上所述,您可以为每个模块/过滤器设置颜色(当然,如果有)。但是你也可以为你所有的设置背景色/前景色!MayaVi2 会话。参见[:秘籍/MayaVi/Tips:秘籍/MayaVi/Tips]。所以,你的背景色和前景色可能(事实上,必须)不同于这里展示的图片。
大纲模块
这个非常简单的模块没有什么特别的。
开始导入大纲模块:
from enthought.mayavi.modules.outline import Outline
然后
fg_color = (0, 0, 0) # black foreground color
o = Outline()
script.add_module(o)
o.actor.property.color = fg_color
[](文件/附件/MayaVi _ scriptingmayavi 2 _ BasicModules/basic _ outline . png
轴模块
对于轴,您可以设置几个参数:
* color for axes;``* color for labels;``* labels name;``* and label format.
以下是您可以键入的内容:
from enthought.mayavi.modules.axes import Axes
然后
a = Axes()
script.add_module(a)
a.axes.property.color = fg_color # color for axes
a.axes.axis_title_text_property.color = fg_color # color for axes title
a.axes.x_label = "Lx" # label for Ox axis
a.axes.y_label = "Ly" # label for Oy axis
a.axes.z_label = "Lz" # label for Oz axis
a.axes.label_format = "" # no dimensions displayed
a.axes.axis_label_text_property.color = fg_color # in case we want to display them
标签格式是沿 Ox、Oy 和 Oz 轴的尺寸格式。默认设置为%6.3g。
[](文件/附件/MayaVi _ scriptingmayavi 2 _ BasicModules/basic _ axes . png
定向克斯模块
!OrientationAxes 模块将在渲染窗口的角落显示一个小的三面体,显示三个轴的方向,Ox,Oy,Oz。
注意:要使用此模块,必须安装 VTK > 5.0。
这里没有什么特别的,作为大纲模块,您可以设置标签颜色:
from enthought.mayavi.modules.orientation_axes import OrientationAxes
和
oa = OrientationAxes()
script.add_module(oa)
oa.text_property.color = fg_color
[](文件/附件/MayaVi _ scriptingmayavi 2 _ BasicModules/basic _ orientonax . png
文本模块
您可以使用此模块显示渲染窗口的标题。
您需要一个文本(一个字符串),并且您必须使用 x_position 和 y_position 参数设置它在窗口中的位置(坐标在 x 和 y 中从 0 到 1)。
然后,您可以设置文本的高度和宽度:
from enthought.mayavi.modules.text import Text
和
# I like my title centered and at top of the window
t = Text()
t.text = "My Title Centered at the Top"
script.add_module(t)
t.actor.scaled_text = False
t.actor.text_property.font_size = 34
t.actor.text_property.color = fg_color
# have to find out text width to center it. Tricky, but works fine. Thanks to Prabhu.
t.width = 1.0*t.actor.mapper.get_width(t.scene.renderer)/t.scene.renderer.size[0]
height = 1.0*t.actor.mapper.get_height(t.scene.renderer)/t.scene.renderer.size[1]
t.x_position = 0.5-t.width/2
t.y_position = 1-height
[](文件/附件/MayaVi _ scriptingmayavi 2 _ BasicModules/basic _ title . png
现在,我们将介绍如何设置颜色条,称为“标量”或“矢量”颜色条。这取决于您文件中的数据。
设置颜色条
严格来说,颜色条不是一个模块,也就是说,您不需要用 add_module()命令来添加它:您必须将“module_manager”对象与先前加载的模块相关联,例如 Text 模块。
然后,您可以按如下方式配置颜色栏(关键词不言自明):
mmsclut = t.module_manager.scalar_lut_manager
mmsclut.show_scalar_bar = True
mmsclutsc = mmsclut.scalar_bar
mmsclutsc.orientation = "vertical" # or "horizontal"
mmsclutsc.width = 0.1
mmsclutsc.height = 0.8
mmsclutsc.position = (0.01, 0.15) # color bar located to the left of the rendering window
mmsclutsc.label_text_property.color = fg_color
mmsclutsc.title_text_property.color = fg_color
mmsclut.number_of_labels = 10
mmsclut.number_of_colors = 64
mmsclut.data_name = "My Label"
[](文件/附件/MayaVi _ scriptingmayavi 2 _ BasicModules/basic _ color bar . png
注意:要为向量而不是标量配置颜色条,请用上面的“向量管理器”替换“标量管理器”。
最后,为了结束“基本”模块部分,让我们看看如何设置场景。
设置场景
通过“设置场景”,你必须阅读“场景将如何被看到”:例如,设置场景的颜色背景和视点。
像往常一样,使用 python & TVTK 设置这些参数非常容易。
如果要更改背景色,可能还需要更改所有模块的前景色。我们在这里回忆他们。
#! /usr/bin/env python
from enthought.mayavi.modules.outline import Outline
from enthought.mayavi.modules.axes import Axes
from enthought.mayavi.modules.orientation_axes import OrientationAxes
from enthought.mayavi.modules.text import Text
# we want a dark foreground color on a bright background
fg_color = (0.06666, 0.06666, 0.1804) # dark blue
bg_color = (1, 1, 0.94118) # ivory
# setting foreground color for Outline module
o = Outline()
script.add_module(o)
o.actor.property.color = fg_color
# setting foreground color for Axes module
a = Axes()
script.add_module(a)
a.axes.property.color = fg_color # color for axes
a.axes.axis_title_text_property.color = fg_color # color for axes label
a.axes.x_label = "Lx" # label for Ox axis
a.axes.y_label = "Ly" # label for Oy axis
a.axes.z_label = "Lz" # label for Oz axis
a.axes.label_format = "" # no dimensions displayed
# setting foreground color for OrientationAxes module
oa = OrientationAxes()
script.add_module(oa)
oa.text_property.color = fg_color
# setting foreground color for Text module
t = Text()
t.text = "My Title Centered at the Top"
script.add_module(t)
t.actor.scaled_text = False
t.actor.text_property.font_size = 34
t.actor.text_property.color = fg_color
t.width = 1.0*t.actor.mapper.get_width(t.scene.renderer)/t.scene.renderer.size[0]
height = 1.0*t.actor.mapper.get_height(t.scene.renderer)/t.scene.renderer.size[1]
t.x_position = 0.5-t.width/2
t.y_position = 1-height
# setting foreground color for labels and title color bar.
mmsclut = t.module_manager.scalar_lut_manager
mmsclut.show_scalar_bar = True
mmsclutsc = mmsclut.scalar_bar
mmsclutsc.orientation = "vertical"
mmsclutsc.width = 0.1
mmsclutsc.height = 0.8
mmsclutsc.position = (0.01, 0.15)
mmsclutsc.label_text_property.color = fg_color
mmsclutsc.title_text_property.color = fg_color
mmsclut.number_of_labels = 10
mmsclut.number_of_colors = 64
mmsclut.data_name = "My Label"
# setting background color for the scene.
t.scene.background = bg_color
有些观点也是在!MayaVi2。
如果你想:
* Ox axis normal to the scene: use x_plus_view() (towards) or x_minus_view() (backwards) method;
* Oy axis normal to the scene: use y_plus_view() (towards) or y_minus_view() (backwards) method;
* Oz axis normal to the scene: use z_plus_view() (towards) or z_minus_view() (backwards) method;
* an isometric view (coordinates normal are (1, 1, 1)), use isometric_view method.
您还可以:
* set the elevation and azimuth angles to your needs (in degrees);
* set a zooming factor of your scene.
与:
t.scene.x_plus_view()
t.scene.camera.azimuth(62)
t.scene.camera.elevation(19.5)
t.scene.camera.zoom(1.5)
最后,您可以选择想要场景的透视图还是平行投影:
t.scene.camera.parallel_projection = True
[](文件/附件/MayaVi _ scriptingmayavi 2 _ BasicModules/basic _ scene _ parall . png
对于平行投影,或者:
t.scene.camera.parallel_projection = False
以获得透视图。
这里,“t”代表先前加载的文本模块。
注意:您可以为场景设置许多其他参数。请参阅[:cook book/MayaVi/Tips:cook book/MayaVi/Tips]了解如何获取有关设置参数模块的更多信息。
现在,是时候阅读最有趣的部分了:配置和使用与数据交互的模块和过滤器。
附件
basic_axes.png
basic_colorbar.png
basic_orientationaxes.png
basic_outline.png
basic_scene_parall.png
basic_scene_persp.png
basic_title.png
编写 Mayavi 2 脚本:过滤器
编写 Mayavi 2 脚本:过滤器
介绍
到目前为止,上面的例子很简单:标量或向量数据呈现在“真空”中,也就是说,没有物体或材料或任何东西。
换句话说,如何在标量或向量字段中显示某个对象,比如金属平行六面体?
您将在这里看到的第一个过滤器处理这个问题。
提取非结构化网格过滤器
对于这个例子,我们假设几个假设:
* the mesh of the volume data is made of 76x60x72 cubic cells (8 vertices); thus, the VTK object cell used for the "vaccum" is called VTK_HEXAHEDRON (#12). Cell ids begins at 0 and ends at #342880.
* a metallic parallellepiped is immersed in a EM field spreading over the whole volume data ; this object is made of faceted cells (4 vertices), called VTK_QUAD (#9) cells in VTK denomination. These faceted cells are used here because of the null EM field within the parallellepiped. Cell ids begin at #342881 and end at #345966.
* as there are different kinds of cells, the !UnstructuredGrid data set must be used (see ```pyhttp://www.vtk.org/pdf/file-formats.pdf
<http://www.vtk.org/pdf/file-formats.pdf>__
要知道如何!必须编写非结构化的网格文件,VTK 单元语法等)
要显示金属平行六面体作为一个独立于真空的物体,你必须提取对应于这个物体的细胞。因此,例如,您将能够使用“曲面”模块显示该对象。
第一,进口!提取未结构化的网格过滤器和曲面模块,如往常一样:
from enthought.mayavi.modules.surface import Surface
from enthought.mayavi.filters.extract_unstructured_grid import ExtractUnstructuredGrid
```py
然后
for the metallic parallellepiped
script.engine.current_object = src # current object must be set to the source
eug1 = ExtractUnstructuredGrid()
script.add_filter(eug1)
eug1.filter.cell_clipping = True
eug1.filter.cell_minimum = 342881
eug1.filter.cell_maximum = 345966
s = Surface() # the metallic is displayed using Surface module
eug1.add_module(s) # the module must be added to the current filter
s.actor.mapper.scalar_visibility = False
s.actor.property.color = (0.509804, 0.5098040, 0.5490196) # grey color for the metallic parallellepiped
we need also extract the required cells for and only for the vaccum
script.engine.current_object = src # current object must be set to the source
eug2 = ExtractUnstructuredGrid()
script.add_filter(eug2)
eug2.filter.cell_clipping = True
eug2.filter.cell_minimum = 0
eug2.filter.cell_maximum = 342880
here, we can display the EM field using ScalarCutPlane/VectorCutPlane,
Surface, Vectors modules as usual
.../...
应该是这样的:
[](文件/附件/Mayavi _ scriptingMayavi2 _ Filters/filter _ eug 1 . png
对于第一个例子,只有一个物体,它是多面的。
现在,假设我们有第二个物体,不是金属的,而是电介质的(所以它里面的电磁场不应该是零)。因此,我们必须使用一些三维单元,如 VTK *六面体单元(单元编号从#345967 到#349094)。我们还想显示金属物体表面的场和电介质物体中的*。
for the metallic parallellepiped
script.engine.current_object = src
eug1 = ExtractUnstructuredGrid()
script.add_filter(eug1)
eug1.filter.cell_clipping = True
eug1.filter.cell_minimum = 342881
eug1.filter.cell_maximum = 345966
s = Surface()
eug1.add_module(s)
s.actor.mapper.scalar_visibility = True # scalar field on the surface
for the dielectric parallellepiped
script.engine.current_object = src
eug2 = ExtractUnstructuredGrid()
script.add_filter(eug2)
eug2.filter.cell_clipping = True
eug2.filter.cell_minimum = 345967
eug2.filter.cell_maximum = 349094
s = Surface()
eug2.add_module(s)
s.actor.mapper.scalar_visibility = True # scalar field set to on
s.enable_contours = True # in the volume
we need also extract the required cells for and only for the vaccum
script.engine.current_object = src # current object must be set to the source
eug3 = ExtractUnstructuredGrid()
script.add_filter(eug3)
eug3.filter.cell_clipping = True
eug3.filter.cell_minimum = 0
eug3.filter.cell_maximum = 342880
.../...
这应该会导致:
[](文件/附件/Mayavi _ scriptingMayavi2 _ Filters/filter _ eug 2 . png
## 提取网格过滤器
使用!提取网格过滤器更容易,因为它(仅)在结构化网格上工作:您只需设置 x、y、z 坐标的最小值/最大值。因此,您可以剪切数据的子体积。您也可以在每个坐标上应用一个比率,以减少单元格的数量。
像往常一样,导入所需的模块和/或过滤器:
from enthought.mayavi.modules.surface import Surface
from enthought.mayavi.filters.extract_grid import ExtractGrid
然后,您可以将过滤器的限制设置为:
eg = ExtractGrid()
script.add_filter(eg)
eg.x_min, eg.x_max = 10, 40
eg.y_min, eg.y_max = 10, 40
eg.z_min, eg.z_max = 10, 40
eg.x_ratio = 2
eg.y_ratio = 2
eg.z_ratio = 2
same example using Surface module
s = Surface()
s.enable_contours = True
s.contour.auto_contours = True
s.contour.number_of_contours = 10
s.actor.property.opacity = 0.2
script.add_module(s)
s.contour.minimum_contour = 0
s.contour.maximum_contour = 1
s.module_manager.scalar_lut_manager.data_range = [0, 1]
[](文件/附件/Mayavi _ scriptingMayavi2 _ Filters/filter _ eg . png
## 阈值滤波器
使用这个过滤器,您可以考虑包含在特定范围内的标量值。
假设您的标量数据从 0 扩展到 1,但是您只对[0.4,0.6]范围内的值感兴趣,并且您想要使用!等值面模块在这个范围内,大约 0.5。根据您的数据范围,默认情况下,滑动条的最小值和最大值将设置为 0 和 1:
[](文件/附件/Mayavi _ scriptingMayavi2 _ Filters/filter _ thr D1 . png
更准确地使用!等值面模块,您必须将最小值和最大值设置为所需值,即 0.4 和 0.6。因此,如果你想看到你的标量数据在 0.5 左右,你可以设置滑动条从 0.4 到 0.6 比滑动条从 0 到 1 更容易。
阈值过滤器可以帮助您做到这一点。
首先开始导入模块和过滤器:
from enthought.mayavi.modules.iso_surface import IsoSurface
from enthought.mayavi.filters.threshold import Threshold
然后,设置阈值:
thh = Threshold()
script.add_filter(thh)
thh.lower_threshold = 0.4
thh.upper_threshold = 0.6
isosurf = IsoSurface()
thh.add_module(isosurf)
isosurf.contour.contours = [0.5]
isosurf.compute_normals = True
isosurf.actor.property.opacity = 0.2
isosurf.module_manager.scalar_lut_manager.data_range = [0, 1]
你完蛋了!
应该是这样的:
[](文件/附件/Mayavi _ scriptingMayavi2 _ Filters/filter _ thrld 2 . png
您可以从前面的两个图中注意到,阈值模块将边界近似为最接近的值(严格来说不等于 0.4 和 0.6)。
## PointToCellData 筛选器
通常,数据在每个点之间进行插值。因此,他们看起来更好。
但也许在某些情况下,您不希望它们被插值,并看到数据“原样”:它们不是显示为点,而是显示为单元格。在这种情况下,您可以使用!PointToCellData 筛选器。
让我们再次看看使用!ScalarCutPlane 模块,并导入!点至细胞数据过滤器:
from enthought.mayavi.modules.scalar_cut_plane import ScalarCutPlane
from enthought.mayavi.filters.point_to_cell_data import PointToCellData
然后添加!ScalarCutPlane 模块“在”上面!像往常一样,指向细胞数据过滤器:
ptocd = PointToCellData()
script.add_filter(ptocd)
scp = ScalarCutPlane()
ptocd.add_module(scp)
scp.implicit_plane.normal = (1, 0, 0)
scp.implicit_plane.origin = (10, 25, 25)
scp.implicit_plane.widget.enabled = False
scp.actor.property.diffuse = 0.0
scp.actor.property.ambient = 1.0
scp.actor.property.opacity = 1.0
scp.module_manager.scalar_lut_manager.data_range = [0, 1]
因此,您可以看到每个单元格上的数据,而不是点(与第一个显示使用!ScalarCutPlane 模块):
![](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/d5a49699.jpg)
## WarpScalar 滤波器
你可以用!例如,WarpScalar 滤镜扭曲 2D 曲面。参见[:Cookbook/MayaVi/Examples:使用 mlab (surf_regular_mlab.py)的示例]。
## 转换数据过滤器
## 附件
* [`filter_eg.png`](../_downloads/filter_eg.jpg)
* [`filter_eug1.png`](../_downloads/filter_eug1.jpg)
* [`filter_eug2.png`](../_downloads/filter_eug2.jpg)
* [`filter_p2c.png`](../_downloads/filter_p2c.jpg)
* [`filter_thrld1.png`](../_downloads/filter_thrld1.jpg)
* [`filter_thrld2.png`](../_downloads/filter_thrld2.jpg)
![http://scipy-cookbook.readthedocs.org/_img/filter_eg.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/ca94dad1.jpg) ![http://scipy-cookbook.readthedocs.org/_img/filter_eug1.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/a8acef5f.jpg) ![http://scipy-cookbook.readthedocs.org/_img/filter_eug2.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/ef0c958f.jpg) ![http://scipy-cookbook.readthedocs.org/_img/filter_p2c.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/d5a49699.jpg) ![http://scipy-cookbook.readthedocs.org/_img/filter_thrld1.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/ce70fb00.jpg) ![http://scipy-cookbook.readthedocs.org/_img/filter_thrld2.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/89d081d0.jpg)
# 编写 Mayavi 2 脚本:主要模块
# 编写 Mayavi 2 脚本:主要模块
## 介绍
这是真正的东西;-)
您将在这里了解如何使用中的各种模块!MayaVi2。
注意:某些模块不能添加到任何类型的数据集。有些工作只为!StructuredGrid or!例如结构点(有关 VTK 数据类型的更多信息,请参见[http://www.vtk.org/pdf/file-formats.pdf](http://www.vtk.org/pdf/file-formats.pdf))。每次需要时都会指定。
注 2:在!在 MayaVi2 树视图中,“主模块”(称为“模块”)已经从“基本模块”加载中分离出来了!ModuleManager。当然,您可以不使用!ModuleManager。
## imageplanedwidget/ScalarCutPlane/slicensstructuredgrid 模块
最简单(也最容易,但不是最令人印象深刻;-))显示 3D 数据的方式无疑是在一些平面上进行切片,垂直于 Ox、Oy 或 Oz 轴,或者是倾斜的。
其中一个你可以用来做这个的模块叫做!ScalarCutPlane。它适用于任何数据。
注意:作为!imageplanewwidget 模块也显示平面上的标量数据(但它不“剪切”数据),请参见[:Cookbook/MayaVi/Examples:以 3D 数组作为数值源(numeric_source.py)的示例]或[:Cookbook/MayaVi/Examples:使用 imageplanewwidget 模块(test.py)的示例]以获取如何使用此模块的更多信息。
您必须设置几个参数:
`* plane normal;``* its origin;``* widget enabled or not: if enabled, you can play with the mouse to modify normal orientation, position, etc.;``* some colors properties.`
因此,您必须键入:
from enthought.mayavi.modules.scalar_cut_plane import ScalarCutPlane
和
scp = ScalarCutPlane() # set scp as ScalarCutPlane() module
script.add_module(scp) # add module to the scene
scp.implicit_plane.normal = (1, 0, 0) # set normal to Ox axis
set origin to (i=10, j=25, k=25) i.e. integers for a structured grid
scp.implicit_plane.origin = (10, 25, 25)
set origin to (x=1.0, y=2.5, z=2.5) i.e. reals for unstructured grids
scp.implicit_plane.origin = (1.0, 2.5, 2.5)
scp.implicit_plane.widget.enabled = False
scp.actor.property.diffuse = 0.0 # set some color properties
scp.actor.property.ambient = 1.0 #
scp.actor.property.opacity = 1.0 #
scp.module_manager.scalar_lut_manager.data_range = [0, 1]
[](文件/附件/Mayavi _ scriptingMayavi2 _ main modules/module _ scp . png
请注意,如果您启用小部件,您将能够“实时”平移(将鼠标移动到红色框架)、更改剖切面的法线(将鼠标移动到灰色箭头):
[]文件/附件/Maya avi _ script ingayavi 2 _ main modules/module _ scp _ widg _ en . png
您也可以将剖切面显示为“扭曲表面”,只需添加几条线,设置要计算的比例因子和法线(更平滑的表面)或不要:
scp.enable_warp_scalar = True
scp.compute_normals = True
scp.warp_scalar.filter.scale_factor = 20
[](文件/附件/Mayavi _ scriptingMayavi2 _ main modules/module _ scp _ warp . png
当然,您可以添加任意数量的剖切面,无论是否倾斜。
现在让我们看一个更复杂的例子:我们希望每个剖切面的不透明度设置为 0.2,并且添加相同剖切面的轮廓(#10)。上面的行已更改如下:
注意:我同意,这不是编写这样一个代码的最好方法。显然,您可以编写一个方法来完成同样的工作。但这不是这里的目的。
cutplane #1, normal to Ox, opacity = 0.2, representation = surface
scp = ScalarCutPlane()
script.add_module(scp)
scp.implicit_plane.normal = (1, 0, 0)
scp.implicit_plane.origin = (25, 25, 25)
scp.implicit_plane.widget.enabled = False
scp.actor.property.diffuse = 0.0
scp.actor.property.ambient = 1.0
scp.actor.property.opacity = 0.2
scp.module_manager.scalar_lut_manager.data_range = [0, 1]
cutplane #2, normal to Oy, opacity = 0.2, representation = surface
scp = ScalarCutPlane()
script.add_module(scp)
scp.implicit_plane.normal = (0, 1, 0)
scp.implicit_plane.origin = (25, 25, 25)
scp.implicit_plane.widget.enabled = False
scp.actor.property.diffuse = 0.0
scp.actor.property.ambient = 1.0
scp.actor.property.opacity = 0.2
scp.module_manager.scalar_lut_manager.data_range = [0, 1]
cutplane #3, normal to Oz, opacity = 0.2, representation = surface
scp = ScalarCutPlane()
script.add_module(scp)
scp.implicit_plane.normal = (0, 0, 1)
scp.implicit_plane.origin = (25, 25, 25)
scp.implicit_plane.widget.enabled = False
scp.actor.property.diffuse = 0.0
scp.actor.property.ambient = 1.0
scp.actor.property.opacity = 0.2
scp.module_manager.scalar_lut_manager.data_range = [0, 1]
cutplane #4, normal to Ox, representation = contour
scp = ScalarCutPlane()
script.add_module(scp)
scp.implicit_plane.normal = (1, 0, 0)
scp.implicit_plane.origin = (25, 25, 25)
scp.implicit_plane.widget.enabled = False
scp.actor.property.diffuse = 0.0
scp.actor.property.ambient = 1.0
scp.enable_contours = True
scp.contour.number_of_contours = 10
scp.contour.minimum_contour, scp.contour.maximum_contour = [0, 1]
scp.module_manager.scalar_lut_manager.data_range = [0, 1]
cutplane #5, normal to Oy, representation = contour
scp = ScalarCutPlane()
script.add_module(scp)
scp.implicit_plane.normal = (0, 1, 0)
scp.implicit_plane.origin = (25, 25, 25)
scp.implicit_plane.widget.enabled = False
scp.actor.property.diffuse = 0.0
scp.actor.property.ambient = 1.0
scp.enable_contours = True
scp.contour.number_of_contours = 10
scp.contour.minimum_contour, scp.contour.maximum_contour = [0, 1]
scp.module_manager.scalar_lut_manager.data_range = [0, 1]
cutplane #6, normal to Oz, representation = contour
scp = ScalarCutPlane()
script.add_module(scp)
scp.implicit_plane.normal = (0, 0, 1)
scp.implicit_plane.origin = (25, 25, 25)
scp.implicit_plane.widget.enabled = False
scp.actor.property.diffuse = 0.0
scp.actor.property.ambient = 1.0
scp.enable_contours = True
scp.contour.number_of_contours = 10
scp.contour.minimum_contour, scp.contour.maximum_contour = [0, 1]
scp.module_manager.scalar_lut_manager.data_range = [0, 1]
看起来像这样:
[](文件/附件/Mayavi _ scriptingMayavi2 _ main modules/module _ scp 2 . png
另一个切片网格的模块叫做!SliceUnstructuredGrid。正如它所称,它应该只适用于非结构化网格。但是,因为它已经在结构化网格上测试过了,甚至!MayaVi2 用一条警告消息来抱怨它,它甚至对结构化网格也“起作用”(对我们的例子来说,令人高兴;-) )
事实上,它的兴趣并不是真正的切片网格,而是更多地展示你的网格的结构,即你的网格单元。这样你就可以看到是否没有任何问题(孔洞等)。).
from enthought.mayavi.modules.slice_unstructured_grid import SliceUnstructuredGrid
和
sug = SliceUnstructuredGrid()
script.add_module(sug)
unstructured grid so origin coordinates are reals
sug.implicit_plane.origin = (25., 25., 25.)
sug.implicit_plane.normal = (1, 1, 1)
sug.implicit_plane.widget.enabled = False
sug.extract_geometry.extract_boundary_cells = False
sug.extract_geometry.extract_inside = True
sug.extract_geometry.extract_only_boundary_cells = False
sug.geom_filter.cell_clipping = False
sug.geom_filter.extent_clipping = False
sug.geom_filter.merging = True
sug.geom_filter.point_clipping = False
sug.actor.property.representation = 'wireframe'
sug.actor.property.diffuse = 0.0
sug.actor.property.ambient = 1.0
sug.actor.property.opacity = 1.0
sug.module_manager.scalar_lut_manager.data_range = [0, 1]
场景应该是这样的:
[](文件/附件/Mayavi _ scriptingMayavi2 _ main modules/module _ sug . png
## grid plane/StructuredGridOutline 模块
使用!GridPlane 模块也会切割你的网格,但与!ScalarCutPlane 模块。你不能只沿着 Ox,Oy 和 Oz 轴得到法线平面,它只适用于结构化网格。但不像!ScalarCutPlane 模块,它总是在一个平面上切割你的网格,!GridPlane 穿过你的网格:如果它是一个共形的网格,切割将不是一个平面,而是一些跟随你的网格曲率的东西。
那个!StructuredGridOutline 模块的作用与 Outline 模块相同,但适用于保形网格。
为了说明我们如何使用这些模块,让我们考虑一个在 VTKData 目录中提供的例子,combxyz.bin & combq.bin 文件(Plot3D 格式)从 tarball vtkdata-5.0.3.tar.gz 你可以下载[这里](http://www.vtk.org/get-software.php#latest)。
所以,输入:
from enthought.mayavi.modules.strucured_grid_outline import StructuredGridOutline
from enthought.mayavi.modules.grid_plane import GridPlane
to load Plot3D files format
from enthought.mayavi.sources.plot3d_reader import PLOT3DReader
和
src = PLOT3DReader()
src.initialize('combxyz.bin', 'combq.bin')
script.add_source(src)
sgo = StructuredGridOutline()
script.add_module(sgo)
gp = GridPlane()
script.add_module(gp)
gp.grid_plane.axis = 'x'
gp.grid_plane.position = 2
gp.actor.mapper.scalar_visibility = True
gp.actor.property.representation = 'surface'
gp.actor.property.diffuse = 0.0
gp.actor.property.ambient = 1.0
gp.actor.property.opacity = 1
gp = GridPlane()
script.add_module(gp)
gp.grid_plane.axis = 'x'
gp.grid_plane.position = 25
gp.actor.mapper.scalar_visibility = True
gp.actor.property.representation = 'surface'
gp.actor.property.diffuse = 0.0
gp.actor.property.ambient = 1.0
gp.actor.property.opacity = 1
gp = GridPlane()
script.add_module(gp)
gp.grid_plane.axis = 'x'
gp.grid_plane.position = 55
gp.actor.mapper.scalar_visibility = True
gp.actor.property.representation = 'surface'
gp.actor.property.diffuse = 0.0
gp.actor.property.ambient = 1.0
gp.actor.property.opacity = 1
场景渲染如下:
[](文件/附件/Mayavi _ scriptingMayavi2 _ main modules/module _ sgo _ gp . png
## 表面/等值面模块
其他模块是 Surface 和!等值面。这些模块可以处理任何数据。
曲面模块与相同!等值面,但会自动显示给定范围内给定数量值的多个等值面。
其实,你可以用得到同样的结果!等值面模块,但你必须设置每个等值面。
当显示几个等值面时,使用 Surface 或!等值面模块,你应该设置不透明度为 1 以下的值,以便看到所有的等值面。
使用 Surface 模块很简单:
from enthought.mayavi.modules.surface import Surface
然后
s = Surface()
s.enable_contours = True # we want contours enabled
s.contour.auto_contours = True # we want isovalues automatically well-defined
s.contour.number_of_contours = 10 # self-explanatory 😉
s.actor.property.opacity = 0.2
script.add_module(s)
s.contour.minimum_contour = 0
s.contour.maximum_contour = 1
s.module_manager.scalar_lut_manager.data_range = [0, 1]
场景应该是这样的:
[](文件/附件/Mayavi _ scriptingMayavi2 _ main modules/module _ surface . png
使用!等值面模块并不难。例如,假设我们希望得到与曲面模块显示相同的结果。
from enthought.mayavi.modules.iso_surface import IsoSurface
和
isosurf = IsoSurface()
script.add_module(isosurf)
isosurf.contour.contours = [0.1111, 0.2222, 0.3333, 0.4444, 0.5555, 0.6666, 0.7777, 0.8888]
isosurf.compute_normals = True
isosurf.actor.property.opacity = 0.2
isosurf.module_manager.scalar_lut_manager.data_range = [0, 1]
当然,这与之前的场景相同,但是现在,您可以分别控制每个等值。
[](文件/附件/Mayavi _ scriptingMayavi2 _ main modules/module _ isosurface . png
有趣的是,您可以为“曲面”或“轮廓”设置最小/最大轮廓!“实时”等值面,移动滑杆。这是一个非常有用的特性。并且可以渲染非常好看的“动态”场景!:-)
## 音量模块
对我来说还是相当实验性的(可以设置很多参数),所以这一节会很短;-)
数据显示在整个体积中,而不是查看表面。
开始导入所需模块:
from enthought.mayavi.modules.volume import Volume
然后,像往常一样将其添加到源中:
v = Volume()
script.add_module(v)
v.lut_manager.show_scalar_bar = True
v.lut_manager.scalar_bar.orientation = 'vertical'
v.lut_manager.scalar_bar.width = 0.1
v.lut_manager.scalar_bar.height = 0.8
v.lut_manager.scalar_bar.position = (0.01, 0.15)
v.lut_manager.scalar_bar.label_text_property.color = fg_color
v.lut_manager.scalar_bar.title_text_property.color = fg_color
v.lut_manager.number_of_labels = 10
v.lut_manager.data_name = ""
请注意,音量模块有一个“颜色转换功能”,这与!其他模块使用的查找表。
渲染后的场景应该是这样的(感谢帕布让 CTF 与 LUT 相似) :
[](文件/附件/Mayavi _ scriptingMayavi2 _ main modules/module _ volume . png
## 矢量/字形/矢量控制平面/作战飞机模块
到目前为止,我们只处理标量值。您也可以将值显示为矢量。您可以使用以下三个模块之一:
`* Vectors module: scale and color are set by vectors data, i.e. a 3D array vectors field;`
`* Glyph module: scale and color are set by scalar data;`
`* !VectorCutPlane module; in this case, vectors are not diplayed in the whole volume, but only on cutplanes, as !ScalarCutPlane module does with scalar values.`
您可以为这些模块设置几个参数,例如箭头形状等。
首先,这取决于你的卷中的点数,但建议你抽取数据。如果你不这样做,你应该什么也看不到,除了到处都是箭,从而失去了相关的信息。你可以选择随机抽取,也可以不抽取。
第二,你可以选择矢量的形状,在下面的列表中:2D 字形或箭头,圆锥,圆柱,球体和立方体三维矢量形状。
第三,您可以为所选形状设置一些参数。例如,使用箭头形状,可以为轴和尖端设置以下属性:
`* the shaft radius;`
`* the shaft resolution (number of polygons);`
`* the tip length;`
`* the tip radius;`
`* the tip resolution;`
您还可以设置矢量位置,在“尾部”、“居中”和“头部”之间,比例模式、颜色模式、比例因子(您的矢量将显示多大)等。
现在让我们看看如何做到这一点。
首先,导入所需的模块。
对于矢量模块,
from enthought.mayavi.modules.vectors import Vectors
对于字形模块,
from enthought.mayavi.modules.glyph import Glyph
为了!向量输出平面模块,
from enthought.mayavi.modules.vector_cut_plane import VectorCutPlane
事实上,您会看到这三个模块使用相同的对象和方法。只有默认值不同。
例如,对于矢量模块,您可以键入:
v = Vectors()
script.add_module(v)
v.glyph.mask_input_points = True # we want to decimate our data...
v.glyph.mask_points.on_ratio = 100 # ...by a ratio of 100
v.glyph.mask_points.random_mode = True # I want a randomly decimation
v.glyph.glyph_source = v.glyph.glyph_list[1] # I like ArrowSource 😉
following values are the default values: tweak your own !
v.glyph.glyph_source.shaft_radius = 0.03
v.glyph.glyph_source.shaft_resolution = 6
v.glyph.glyph_source.tip_length = 0.35
v.glyph.glyph_source.tip_radius = 0.1
v.glyph.glyph_source.tip_resolution = 6
v.glyph.glyph.scale_factor = 10
v.glyph.glyph_position = 'tail'
v.glyph.scale_mode = 'scale_by_vector'
v.glyph.color_mode = 'color_by_vector'
if you use Glyph module, here are the default values
v.glyph.glyph_position = 'center'
v.glyph.scale_mode = 'scale_by_scalar'
v.glyph.color_mode = 'color_by_scalar'
如果我们再考虑一下;-),与之前已经显示的 3D 数据相同,但这次,使用矢量而不是标量数据,场景应该如下所示:
[](文件/附件/Mayavi _ scriptingMayavi2 _ main modules/module _ vectors . png
为了!VectorCutPlane 模块,可以设置与上面相同的属性加上!ScalarCutPlane 模块,如 implicit_plane.normal、implicit_plane.origin、implicit_plane.widget.enabled 等:
vcp = VectorCutPlane()
script.add_module(vcp)
vcp.glyph.mask_input_points = True
vcp.glyph.mask_points.on_ratio = 5
vcp.glyph.mask_points.random_mode = False
vcp.glyph.glyph_source = vcp.glyph.glyph_list[1]
vcp.glyph.glyph_source.shaft_radius = 0.03
vcp.glyph.glyph_source.shaft_resolution = 6
vcp.glyph.glyph_source.tip_length = 0.35
vcp.glyph.glyph_source.tip_radius = 0.1
vcp.glyph.glyph_source.tip_resolution = 6
vcp.glyph.glyph.scale_factor = 20
vcp.glyph.glyph_position = 'tail'
vcp.glyph.scale_mode = 'scale_by_vector'
vcp.glyph.color_mode = 'color_by_vector'
vcp.implicit_plane.normal = (1, 0, 0) # set normal to Ox axis
vcp.implicit_plane.origin = (10, 25, 25) # set origin to (i=10, j=25, k=25) for a structured grid
vcp.implicit_plane.widget.enabled = True
vcp.actor.property.diffuse = 0.0 # set some color properties
vcp.actor.property.ambient = 1.0 #
vcp.actor.property.opacity = 1.0 #
vcp.module_manager.vector_lut_manager.data_range = [0, 1]
这将渲染此场景:
[](文件/附件/Mayavi _ scriptingMayavi2 _ main modules/module _ VCP . png
您也可以根据向量字段扭曲剖切面。为此,您必须加载另一个模块,而不是!VectorCutPlane,叫!WarpVectorCutPlane。
类型:
from enthought.mayavi.modules.warp_vector_cut_plane import WarpVectorCutPlane
然后
wvcp = WarpVectorCutPlane()
script.add_module(wvcp)
wvcp.implicit_plane.normal = (1, 0, 0) # set normal to Ox axis
wvcp.implicit_plane.origin = (10, 25, 25) # set origin to (i=10, j=25, k=25) for a structured grid
wvcp.implicit_plane.widget.enabled = True
wvcp.compute_normals = True
wvcp.warp_vector.filter.scale_factor = 10
你应该得到这个(与扭曲的表面相比!ScalarCutPlane 模块):
[](文件/附件/Mayavi _ scriptingMayavi2 _ main modules/module _ warpvcp . png
## 流线模块
显示向量字段的另一种方法是使用流线模块。
我们这里考虑其他的 Plot3D 文件:posxyz . bin & postq . bin 你可以在这里下载[。你可以在 VTK 主页](http://www.vtk.org/files/VTKTextbook/Data.tgz)[这里](http://www.vtk.org)找到一些使用这些文件的截图。
您可以为此模块设置几个参数:例如,流线的类型(管、带或线)及其属性,以及“种子”。
我们也用了!本例中的 GridPlane 模块:
开始导入所需模块:
from enthought.mayavi.sources.plot3d_reader import PLOT3DReader
from enthought.mayavi.modules.streamline import Streamline
from enthought.mayavi.modules.grid_plane import GridPlane
在本例中,我们希望流线显示为具有 10 条边的管,并且种子设置为线种子。我们还选择显示 Plot3D 文件的“动能”部分。
src = PLOT3DReader()
src.initialize('postxyz.bin', 'postq.bin')
src.scalars_name = "kinetic energy"
script.add_source(src)
gp = GridPlane()
script.add_module(gp)
gp.grid_plane.axis = 'x'
gp.actor.mapper.scalar_visibility = True
gp.actor.property.representation = 'surface'
gp.actor.property.diffuse = 0.0
gp.actor.property.ambient = 1.0
gp.actor.property.opacity = 1
gp = GridPlane()
script.add_module(gp)
gp.grid_plane.axis = 'z'
gp.actor.mapper.scalar_visibility = False
gp.actor.property.representation = 'wireframe'
gp.actor.property.diffuse = 0.0
gp.actor.property.ambient = 1.0
gp.actor.property.opacity = 1
strl = Streamline()
script.add_module(strl)
strl.streamline_type = "tube" # tube, ribbon or line
strl.tube_filter.number_of_sides = 10
strl.tube_filter.radius = 0.1
strl.seed.widget = strl.seed.widget_list[1] # [Sphere, Line, Plane, Point]
strl.seed.widget.align = "z_axis" # or "x_axis", "y_axis"
strl.seed.widget.point1 = (-0.7, 0, 0)
strl.seed.widget.point2 = (-0.7, 0, 4.82)
strl.seed.widget.resolution = 10
strl.seed.widget.enabled = False
这应该看起来像:
![](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/109a955e.jpg)
注意:您还可以在[:Cookbook/MayaVi/Examples:Cookbook/MayaVi/Examples]中看到使用流线模块的示例。
## 附件
* [`module_isosurface.png`](../_downloads/module_isosurface.jpg)
* [`module_scp.png`](../_downloads/module_scp.jpg)
* [`module_scp2.png`](../_downloads/module_scp2.jpg)
* [`module_scp_warp.png`](../_downloads/module_scp_warp.jpg)
* [`module_scp_widg_en.png`](../_downloads/module_scp_widg_en.jpg)
* [`module_sgo_gp.png`](../_downloads/module_sgo_gp.jpg)
* [`module_streamline.png`](../_downloads/module_streamline.jpg)
* [`module_sug.png`](../_downloads/module_sug.jpg)
* [`module_surface.png`](../_downloads/module_surface.jpg)
* [`module_vcp.png`](../_downloads/module_vcp.jpg)
* [`module_vectors.png`](../_downloads/module_vectors.jpg)
* [`module_volume.png`](../_downloads/module_volume.jpg)
* [`module_warpvcp.png`](../_downloads/module_warpvcp.jpg)
![http://scipy-cookbook.readthedocs.org/_img/module_isosurface.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/4e157a28.jpg)![http://scipy-cookbook.readthedocs.org/_img/module_scp.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/1835f378.jpg)![http://scipy-cookbook.readthedocs.org/_img/module_scp2.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/16aae0aa.jpg)![http://scipy-cookbook.readthedocs.org/_img/module_scp_warp.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/b6c08576.jpg)![http://scipy-cookbook.readthedocs.org/_img/module_scp_widg_en.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/4b9fa547.jpg)![http://scipy-cookbook.readthedocs.org/_img/module_sgo_gp.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/25afe95e.jpg)![http://scipy-cookbook.readthedocs.org/_img/module_streamline.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/109a955e.jpg)![http://scipy-cookbook.readthedocs.org/_img/module_sug.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/1f7a596c.jpg)![http://scipy-cookbook.readthedocs.org/_img/module_surface.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/4c6d153c.jpg)![http://scipy-cookbook.readthedocs.org/_img/module_vcp.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/4a0ddcdf.jpg)![http://scipy-cookbook.readthedocs.org/_img/module_vectors.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/e8a490b3.jpg)![http://scipy-cookbook.readthedocs.org/_img/module_volume.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/9487daa7.jpg)![http://scipy-cookbook.readthedocs.org/_img/module_warpvcp.jpg](https://github.com/OpenDocCN/geekdoc-ds-zh/raw/master/sp-cb-fix1/img/625cc532.jpg)
# 二十、性能
* [使用 Python 进行性能计算的初学者指南](PerformancePython.html)
* [带 numpy 和 scipy 的并行编程](ParallelProgramming.html)
# 使用 Python 进行性能计算的初学者指南
# 使用 Python 进行性能计算的初学者指南
用 NumPy、Pyrex、Psyco、Fortran (77 和 90)和 C++求解拉普拉斯方程的编织比较。这篇文章最初是由帕布·拉玛钱德朗写的。
[`laplace.py`](../_downloads/laplace.py) 是下面讨论的完整 Python 代码。源 tarball ( [`perfpy_2.tgz`](../_downloads/perfpy_2.tgz) )还包含 Fortran 代码、纯 C++代码、Pyrex 源和用于构建 f2py 和 Pyrex 模块的 setup.py 脚本。
## 介绍
这是一个使用 Python 进行性能计算的简单介绍性文档。我们将使用 NumPy、SciPy 的编织(同时使用 weave.blitz 和 weave.inline)和 Pyrex。我们还将展示如何使用 f2py 包装一个 Fortran 子例程,并从 Python 内部调用它。
我们还将利用这个机会,用 Python 对解决特定数值问题的各种方法进行基准测试,并将它们与 C++中的算法实现进行比较。
## 问题描述
我们将考虑的例子是使用迭代有限差分格式(四点平均法、高斯-塞德尔法或高斯-乔丹法)求解 2D 拉普拉斯方程的一个非常简单(简单)的例子。问题的正式说明如下。我们需要求解一些未知函数 u(x,y ),使得∇2u = 0,并指定边界条件。为了方便起见,感兴趣的区域被认为是一个矩形,并且给出了这个矩形的边的边界值。
可以表明,这个问题可以通过使用简单的四点平均方案来解决,如下所示。将域离散成一个(nx x ny)点网格。那么函数 u 可以表示为二维数组 u(nx,ny)。给出了沿矩形边的 u 值。解决方案可以通过以下方式迭代获得。
```py
for i in range(1, nx-1):
for j in range(1, ny-1):
u[i,j] = ((u[i-1, j] + u[i+1, j])*dy**2 +
(u[i, j-1] + u[i, j+1])*dx**2)/(2.0*(dx**2 + dy**2))
其中 dx 和 dy 是离散化域沿 x 和 y 轴的长度。
数解
在纯 Python 中,实现求解器是非常简单的。使用一个简单的 NumPy 数组来存储解矩阵 u。
import numpy
class Grid:
"""A simple grid class that stores the details and solution of the
computational grid."""
def __init__(self, nx=10, ny=10, xmin=0.0, xmax=1.0,
ymin=0.0, ymax=1.0):
self.xmin, self.xmax, self.ymin, self.ymax = xmin, xmax, ymin, ymax
self.dx = float(xmax-xmin)/(nx-1)
self.dy = float(ymax-ymin)/(ny-1)
self.u = numpy.zeros((nx, ny), 'd')
# used to compute the change in solution in some of the methods.
self.old_u = self.u.copy()
def setBCFunc(self, func):
"""Sets the BC given a function of two variables."""
xmin, ymin = self.xmin, self.ymin
xmax, ymax = self.xmax, self.ymax
x = numpy.arange(xmin, xmax + self.dx*0.5, self.dx)
y = numpy.arange(ymin, ymax + self.dy*0.5, self.dy)
self.u[0 ,:] = func(xmin,y)
self.u[-1,:] = func(xmax,y)
self.u[:, 0] = func(x,ymin)
self.u[:,-1] = func(x,ymax)
def computeError(self):
"""Computes absolute error using an L2 norm for the solution.
This requires that self.u and self.old_u must be appropriately
setup."""
v = (self.u - self.old_u).flat
return numpy.sqrt(numpy.dot(v,v))
class LaplaceSolver:
"""A simple Laplacian solver that can use different schemes to
solve the problem."""
def __init__(self, grid, stepper='numeric'):
self.grid = grid
self.setTimeStepper(stepper)
def slowTimeStep(self, dt=0.0):
"""Takes a time step using straight forward Python loops."""
g = self.grid
nx, ny = g.u.shape
dx2, dy2 = g.dx**2, g.dy**2
dnr_inv = 0.5/(dx2 + dy2)
u = g.u
err = 0.0
for i in range(1, nx-1):
for j in range(1, ny-1):
tmp = u[i,j]
u[i,j] = ((u[i-1, j] + u[i+1, j])*dy2 +
(u[i, j-1] + u[i, j+1])*dx2)*dnr_inv
diff = u[i,j] - tmp
err += diff*diff
return numpy.sqrt(err)
def setTimeStepper(self, stepper='numeric'):
"""Sets the time step scheme to be used while solving given a
string which should be one of ['slow', 'numeric', 'blitz',
'inline', 'fastinline', 'fortran']."""
if stepper == 'slow':
self.timeStep = self.slowTimeStep
# ...
else:
self.timeStep = self.numericTimeStep
def solve(self, n_iter=0, eps=1.0e-16):
err = self.timeStep()
count = 1
while err > eps:
if n_iter and count >= n_iter:
return err
err = self.timeStep()
count = count + 1
return count
代码非常简单,非常容易编写,但是如果我们运行它来解决任何大问题(比如 500 x 500 个点的网格),我们会发现它需要永远才能运行。这种情况下的 CPU 占道就是slowTimeStep
法。在下一节中,我们将使用 NumPy 加速它。
使用 NumPy
原来LaplaceSolver.slowTimeStep
方法的最内循环可以很容易地用简单得多的 NumPy 表达式来表示。这是一个重写的时间步长方法。
def numericTimeStep(self, dt=0.0):
"""Takes a time step using a NumPy expression."""
g = self.grid
dx2, dy2 = g.dx**2, g.dy**2
dnr_inv = 0.5/(dx2 + dy2)
u = g.u
g.old_u = u.copy() # needed to compute the error.
# The actual iteration
u[1:-1, 1:-1] = ((u[0:-2, 1:-1] + u[2:, 1:-1])*dy2 +
(u[1:-1,0:-2] + u[1:-1, 2:])*dx2)*dnr_inv
return g.computeError()
整个 for i 和 j 循环已经被一个 NumPy 表达式所取代。NumPy 表达式是元素式的,因此上面的表达式是有效的。它基本上计算四点平均值。如果你已经完成了 NumPy 教程并玩了一点NumPy
,你应该能够理解这是如何工作的。这个表达式的美妙之处在于它完全是用 c 语言完成的。这使得计算比快得多。为了快速比较,这里有一些 500x500 网格上的单次迭代的数字。在具有 192 兆内存的 PIII 450 兆赫兹上,上面的过程需要大约 0.3 秒,而前一个过程需要大约 15 秒。这接近 50 倍的速度提升。你也会注意到一些事情。
- 我们不能像前面在 for 循环中那样计算错误。我们需要复制一份数据,然后使用 computeError 函数来完成这项工作。这耗费了我们的记忆,也不是很美好。这当然是一个限制,但值得增加 50 倍的速度。
- 表达式将使用临时变量。因此,在一次迭代期间,在已经计算的位置的计算值将不在迭代期间使用。例如,在原始 For 循环中,一旦计算出 u[1,1]的值,u[1,2]的下一个值将使用新计算的 u[1,1],而不是旧的值。但是,由于 NumPy 表达式在内部使用临时变量,因此将只使用 u[1,1]的旧值。在这种情况下,这不是一个严重的问题,因为众所周知,即使发生这种情况,算法也会收敛(但收敛时间是原来的两倍,这将收益减少了 2 倍,这仍然使我们的收益增加了 25 倍)。
除了这两个问题之外,很明显,使用 NumPy 可以极大地提高速度。我们现在将使用惊人的编织包来进一步加快速度。
使用编织。闪电战
如果我们使用 weave.blitz,NumPy 表达式可以大大加快速度。
# import necessary modules and functions
from scipy import weave
# ...
def blitzTimeStep(self, dt=0.0):
"""Takes a time step using a NumPy expression that has been
blitzed using weave."""
g = self.grid
dx2, dy2 = g.dx**2, g.dy**2
dnr_inv = 0.5/(dx2 + dy2)
u = g.u
g.old_u = u.copy()
# The actual iteration
expr = "u[1:-1, 1:-1] = ((u[0:-2, 1:-1] + u[2:, 1:-1])*dy2 + "\
"(u[1:-1,0:-2] + u[1:-1, 2:])*dx2)*dnr_inv"
weave.blitz(expr, check_size=0)
return g.computeError()
如果您注意到了,唯一改变的是我们在原始数值表达式周围加上引号,并调用这个字符串“expr”,然后调用 weave.blitz。当设置为 1 时,“check_size”关键字会进行一些健全性检查,并将在您调试代码时使用。然而,对于纯速度,将其设置为 0 是明智的。这一次,当我们对 500x500 数组的代码进行一次迭代时,只需要大约 0.1 秒,大约增加了三倍!还有几件事需要注意。
- 第一次调用这个方法,需要很长时间才能在背后做一些神奇的事情。下次调用它时,它将立即运行。编织文档中有更多的细节。基本上,weave.blitz 将 NumPy 表达式转换为 C++代码,并将 blitz++用于数组表达式,构建一个 Python 模块,将其存储在一个特殊的位置,并在下次进行函数调用时调用该模块。
- 同样,我们需要使用临时数组来计算错误。
- blitz 不使用而使用进行计算,因此其行为更像循环的原始(慢)方式,因为计算的值会被立即重用。
除了这些点之外,与原始的 for 循环相比,结果是相同的。只比原代码快 170 倍左右!我们现在将看看另一种方法来加快我们最初的 for 循环。输入 weave.inline!
使用编织。直列
内联允许将 C 或 C++代码直接嵌入到 Python 代码中。下面是代码内联版本的简单版本。
from scipy.weave import converters
# ...
def inlineTimeStep(self, dt=0.0):
"""Takes a time step using inlined C code -- this version uses
blitz arrays."""
g = self.grid
nx, ny = g.u.shape
dx2, dy2 = g.dx**2, g.dy**2
dnr_inv = 0.5/(dx2 + dy2)
u = g.u
code = """
#line 120 "laplace.py" (This is only useful for debugging)
double tmp, err, diff;
err = 0.0;
for (int i=1; i<nx-1; ++i) {
for (int j=1; j<ny-1; ++j) {
tmp = u(i,j);
u(i,j) = ((u(i-1,j) + u(i+1,j))*dy2 +
(u(i,j-1) + u(i,j+1))*dx2)*dnr_inv;
diff = u(i,j) - tmp;
err += diff*diff;
}
}
return_val = sqrt(err);
"""
# compiler keyword only needed on windows with MSVC installed
err = weave.inline(code,
['u', 'dx2', 'dy2', 'dnr_inv', 'nx', 'ny'],
type_converters=converters.blitz,
compiler = 'gcc')
return err
代码本身看起来非常简单(这就是内联如此酷的原因)。内联函数调用参数都是不言自明的。带' # 120 行...'的行只用于调试,无论如何不影响速度。同样,第一次运行这个函数时,需要花很长时间在幕后做一些事情,下一次它就会消失。这次请注意,我们的循环具有更大的灵活性,可以轻松计算错误项,而不需要临时数组。为这个版本计时会导致 500x500 数组每次迭代的时间仅为 0.04 秒!这相当于一个巨大的 375 倍的速度增加了平原旧的循环。请记住,我们没有牺牲 Python 不可思议的灵活性!这个循环包含看起来非常好的代码,但是如果我们愿意,我们可以通过编写一点脏代码来进一步加快速度。我们在这里不讨论这个问题,但是可以说,通过使用不同的方法,有可能获得 2 倍的速度。这个代码基本上是对 NumPy 数组数据进行指针运算,而不是使用 blitz++数组。这段代码是埃里克·琼斯贡献的。本文附带的源代码包含此代码。
接下来,我们看看如何在 Fortran 中轻松实现循环,并通过使用 f2py 从 Python 中调用它。
使用 f2py
f2py 是一个惊人的实用程序,可以让你轻松地从 Python 中调用 Fortran 函数。首先,我们将编写一个小的 Fortran77 子程序来进行计算。这是代码。
c File flaplace.f
subroutine timestep(u,n,m,dx,dy,error)
double precision u(n,m)
double precision dx,dy,dx2,dy2,dnr_inv,tmp,diff
integer n,m,i,j
cf2py intent(in) :: dx,dy
cf2py intent(in,out) :: u
cf2py intent(out) :: error
cf2py intent(hide) :: n,m
dx2 = dx*dx
dy2 = dy*dy
dnr_inv = 0.5d0 / (dx2+dy2)
error = 0
do 200,j=2,m-1
do 100,i=2,n-1
tmp = u(i,j)
u(i,j) = ((u(i-1,j) + u(i+1,j))*dy2+
& (u(i,j-1) + u(i,j+1))*dx2)*dnr_inv
diff = u(i,j) - tmp
error = error + diff*diff
100 continue
200 continue
error = sqrt(error)
end
以 cf2py 开头的行是特殊的 f2py 指令,记录在 f2py 中。对于懂一些 Fortran 的人来说,剩下的代码很简单。我们通常使用以下命令为此创建一个 Python 模块。
% f2py -c flaplace.f -m flaplace
下面是 Python 的外观。
import flaplace
def fortranTimeStep(self, dt=0.0):
"""Takes a time step using a simple fortran module that
implements the loop in Fortran. """
g = self.grid
g.u, err = flaplace.timestep(g.u, g.dx, g.dy)
return err
就是这样!希望有一天 scipy.weave 能让我们在线完成这项工作,而不需要我们编写单独的 Fortran 文件。Fortran 代码和 f2py 示例由 f2py 的作者 Pearu Peterson 提供。无论如何,使用这个模块,对于 500x500 的网格,每次迭代大约需要 0.029 秒!这比原始代码的速度提高了大约 500 倍。
f2py 还可以与更现代的 Fortran 版本一起工作。这很有用,因为 Fortran90 有特殊的数组特性,允许更紧凑、更好、更快的代码。Fortran90 中有相同的子程序:
! File flaplace90_arrays.f90
subroutine timestep(u,n,m,dx,dy,error)
implicit none
!Pythonic array indices, from 0 to n-1.
real (kind=8), dimension(0:n-1,0:m-1), intent(inout):: u
real (kind=8), intent(in) :: dx,dy
real (kind=8), intent(out) :: error
integer, intent(in) :: n,m
real (kind=8), dimension(0:n-1,0:m-1) :: diff
real (kind=8) :: dx2,dy2,dnr_inv
!f2py intent(in) :: dx,dy
!f2py intent(in,out) :: u
!f2py intent(out) :: error
!f2py intent(hide) :: n,m
dx2 = dx*dx
dy2 = dy*dy
dnr_inv = 0.5d0 / (dx2+dy2)
diff=u
u(1:n-2, 1:m-2) = ((u(0:n-3, 1:m-2) + u(2:n-1, 1:m-2))*dy2 + &
(u(1:n-2,0:m-3) + u(1:n-2, 2:m-1))*dx2)*dnr_inv
error=sqrt(sum((u-diff)**2))
end subroutine
请注意,这些操作是在一行中执行的,非常类似于 Numpy。编译步骤完全相同。在 1000x1000 的网格中,这段代码比 fortran77 循环快 2.2 倍。
源 tarball(perppy _ 2 . tgz)还包含一个使用 forall 构造的 Fortran95 子例程,其性能非常相似。
使用 Pyrex/Cython
我们还使用快速内联版本的代码在 Pyrex 中实现了 timeStep 函数。Pyrex 源代码比 weave、blitz 或 Fortran 代码稍长,因为我们必须公开 NumPy 数组结构。基本功能如下。
特拉维斯·奥列芬特写了一个 Cython 版本的例子。
def pyrexTimeStep(ndarray u, double dx, double dy):
if chr(u.descr.type) <> "d":
raise TypeError("Double array required")
if u.nd <> 2:
raise ValueError("2 dimensional array required")
cdef int nx, ny
cdef double dx2, dy2, dnr_inv, err
cdef double *elem
nx = u.dimensions[0]
ny = u.dimensions[1]
dx2, dy2 = dx**2, dy**2
dnr_inv = 0.5/(dx2 + dy2)
elem = u.data
err = 0.0
cdef int i, j
cdef double *uc, *uu, *ud, *ul, *ur
cdef double diff, tmp
for i from 1 <= i < nx-1:
uc = elem + i*ny + 1
ur = elem + i*ny + 2
ul = elem + i*ny
uu = elem + (i+1)*ny + 1
ud = elem + (i-1)*ny + 1
for j from 1 <= j < ny-1:
tmp = uc[0]
uc[0] = ((ul[0] + ur[0])*dy2 +
(uu[0] + ud[0])*dx2)*dnr_inv
diff = uc[0] - tmp
err = err + diff*diff
uc = uc + 1; ur = ur + 1; ul = ul + 1
uu = uu + 1; ud = ud + 1
return sqrt(err)
这个函数看起来很长,但写起来并不太难。通过提供方便的函数来访问数组,也可以在不进行指针运算的情况下进行写入。然而,上面显示的代码很快。本文提供的源代码包含完整的 Pyrex 文件和一个 setup.py 脚本来构建它。计时这个版本,我们发现这个版本和快速内联版本一样快,只需要 0.025 秒。
利用 Matlab 和 Octave
我们已经在 Matlab 和 Octave 中实现了数字版本( laplace.m )并在不同的计算机上运行测试(因此得到下表中的“估计”值)。我们发现,在 Matlab 中没有获得显著的加速,而 Octave 的运行速度比NumPy
慢两倍。详细的图表可以在这里找到。
C++中的一个实现
最后,作为比较,我们在没有任何 Python 的情况下,用简单的 C++实现了这个。人们会期望 C++代码会更快,但令人惊讶的是,不会太快!考虑到使用 Python 开发非常容易,这种速度上的降低并不是很明显。
最后的比较
以下是 100 次迭代的 500x500 网格的一些时序结果。请注意,我们还比较了将慢速 Python 版本与 Psyco 一起使用的结果。
解决方案类型|花费的时间(秒)—|—Python(估计)|1500.0Python + Psyco(估计)|1138.0Python + NumPy
表达式| 29.3 blitz | 9.5 Inline | 4.3 fast Inline | 2.3 Tyson/Fortran | 2.9 pyrex | 2.5 atlab(估计)| 29.0Octave(估计)| 60.0Pure C++ | 2.16
考虑到 Python 的灵活性和强大功能,这是相当惊人的。
在这里下载本指南的源代码: perfpy_2.tgz
查看完整的 Python 代码示例: laplace.py
查看完整的 Matlab/Octave 代码示例: laplace.m
附件
用 numpy 和 scipy 进行并行编程
用 numpy 和 scipy 进行并行编程
多处理器和多核机器变得越来越普遍,利用它们让代码运行得更快会很好。numpy/scipy 在这方面并不完美,但有些事情你可以做。利用并行处理系统的最佳方式取决于您正在执行的任务和您正在使用的并行系统。如果你有一个大的复杂的工作或一群机器,充分利用将需要很多思考。但是许多任务可以用一种相当简单的方式并行化。
俗话说,“过早优化是万恶之源”。使用多核机器最多可以提供可用内核数量的一倍加速。在考虑并行化之前,先让你的代码工作起来。然后问问你自己你的代码是否真的需要更快。除非迫不得已,否则不要走上充满 bug 的并行化之路。
简单并行化
将您的工作分解成更小的工作,并同时运行它们
例如,如果你正在分析来自脉冲星调查的数据,并且你有数千束光束要分析,每束花一天的时间,最简单的(也可能是最有效的)并行化任务的方法是简单地运行每束光束作为一项工作。有两个处理器的机器只能运行两个作业。无需担心锁定或通信;不需要编写知道并行运行的代码。如果每个进程都需要和你的机器一样多的内存,或者它们都是 IO 重的,你可能会有问题,但是总的来说,这是一个简单有效的并行化你的代码的方法——如果它有效的话。并不是所有的任务都能很好地完成。如果您的目标是处理单个图像,那么不需要大量工作就不清楚如何完成。
使用并行原语
numpy 的一大优势是您可以非常清晰地表达数组操作。例如,要计算矩阵 A 和矩阵 B 的乘积,只需:
>>> C = numpy.dot(A,B)
这不仅读写简单明了,因为 numpy 知道你想做一个矩阵点积,它可以使用作为“BLAS”(基本线性代数子程序)的一部分获得的优化实现。这通常是一个经过精心调整的库,通过利用高速缓冲存储器和汇编器实现在您的硬件上尽可能快地运行。但是现在许多架构都有 BLAS,它也利用了多核机器。如果您的 numpy/scipy 是使用其中的一个编译的,那么 dot()将被并行计算(如果这样更快的话),而您不需要做任何事情。类似的还有其他矩阵运算,像求逆、奇异值分解、行列式等等。例如,开源库 ATLAS 允许在编译时选择并行度(线程数)。英特尔专有的 MKL 库提供了在运行时选择并行级别的可能性。还有 GOTO 库,允许运行时选择并行级别。这是一个商业产品,但源代码是免费分发的学术用途。
最后,scipy/numpy 不会像
>>> A = B + C
>>> A = numpy.sin(B)
>>> A = scipy.stats.norm.isf(B)
这些操作按顺序运行,没有利用多核机器的优势(见下文)。原则上,这可以在不做太多工作的情况下改变。OpenMP 是 C 语言的扩展,它允许编译器为适当注释的循环(和其他东西)生成并行代码。如果有人坐下来在 numpy 中注释了几个核心循环(也可能在 scipy 中),然后在 OpenMP 打开的情况下编译了 numpy/scipy,那么上述三个循环将自动并行运行。当然,在现实中,人们会想要一些运行时控制——例如,如果计划在同一台多处理器机器上运行几个作业,人们可能想要关闭自动并行化。
编写多线程或多处理代码
有时你可以看到如何将你的问题分解成几个并行的任务,但是这些任务需要某种同步或通信。例如,您可能有一个可以并行运行的作业列表,但是您需要收集它们的所有结果,进行一些汇总计算,然后启动另一批并行作业。在 Python 中有两种方法可以做到这一点,使用多个[线程](http://en.wikipe%20dia.org/wiki/Thread_(computer_science)或[进程](http://en.wikipedia.org/wiki/Process_(computing)。
线
线程通常比进程“轻”,并且可以比进程更快地创建、销毁和切换。它们通常是利用多核系统的首选。但是,python 的多线程有一个关键的限制;全局解释器锁定 ( GIL )。由于各种原因(快速的网络搜索会出现大量的讨论,更不用说争论,关于为什么会存在和这是否是个好主意,python 的实现方式是一次只能有一个线程访问解释器。这基本上意味着一次只能有一个线程运行 python 代码。这几乎意味着你根本没有利用并行处理的任何优势。例外很少,但很重要:当一个线程在等待 IO 时(比如,让你键入一些东西,或者等待网络中出现一些东西),python 会释放 GIL,这样其他线程就可以运行了。对我们来说更重要的是,当 numpy 进行数组操作时,python 也发布了 GIL。因此,如果你告诉一个线程去做:
>>> print "%s %s %s %s and %s" %( ("spam",) *3 + ("eggs",) + ("spam",) )
>>> A = B + C
>>> print A
在打印操作和%格式化操作期间,没有其他线程可以执行。但是在 A = B + C 期间,另一个线程可以运行——如果您已经用 numpy 风格编写了代码,那么大部分计算将在 A = B + C 这样的一些数组操作中完成。因此,您实际上可以通过使用多个线程来获得加速。
python 线程模块是标准库的一部分,并提供多线程工具。鉴于上面讨论的限制,在多线程架构中仔细重写代码可能不值得。但是有时候你可以不费吹灰之力完成多线程,在这些情况下,这是值得的。一个例子见Cookbook/Multithreading
。这个秘籍提供了一个线程池()接口,该接口的 API 与进程(如下)的 API 相同,这也是我们感兴趣的。还有非常类似的 ThreadPo ol 模块。
处理
克服上述 GIL 限制的一种方法是使用多个完整进程,而不是线程。每个进程都有自己的 GIL,所以它们不会像线程那样互相阻塞。从 python 2.6 开始,标准库包括一个多处理模块,与线程模块具有相同的接口。对于 Python 的早期版本,这可以作为处理模块获得(python 2.6 的多处理模块的一个后端口,适用于 python 2.4 和 2.5,正在开发中:多处理)。可以在进程之间共享内存,包括 numpy 数组。这使得线程的大部分好处没有 GIL 的问题。它还提供了一个简单的 Pool()接口,该接口的功能是映射和应用类似于Cookbook/Multithreading
示例中的命令。
比较
下面是一个非常基本的比较,它说明了 GIL(在双核机器上)的效果。
import numpy as np
import math
def f(x):
print x
y = [1]*10000000
[math.exp(i) for i in y]
def g(x):
print x
y = np.ones(10000000)
np.exp(y)
from handythread import foreach
from processing import Pool
from timings import f,g
def fornorm(f,l):
for i in l:
f(i)
time fornorm(g,range(100))
time fornorm(f,range(10))
time foreach(g,range(100),threads=2)
time foreach(f,range(10),threads=2)
p = Pool(2)
time p.map(g,range(100))
time p.map(f,range(100))
| 100g()| 10f()—|—|—正常| 43.5s
| 48s
2 线程| 31s
| 71.5s
2 进程| 27s
| 31.23
对于不释放 GIL 的函数f()
,线程实际上比串行代码表现更差,大概是由于上下文切换的开销。然而,使用两个进程确实提供了显著的加速。对于使用 numpy 并释放 GIL 的函数g()
,线程和进程都提供了显著的速度提升,尽管多进程稍快。
复杂的并行化
如果你需要复杂的并行性——比如说,你有一个计算集群,你的工作需要经常相互交流——你就需要开始考虑真正的并行编程。这是计算机科学研究生课程的一个主题,我不打算在这里讨论它。但是有一些 python 工具可以用来实现你在研究生课程中学到的东西。(我可能有些夸张——有些并行化并没有那么难,其中一些工具让它变得相当容易。但是要意识到并行代码比串行代码更难编写和调试。)
二十一、科学图形用户界面
wxPython 对话方块
wxPython 对话方块
我喜欢 matlab 中简单的文件选择器对话框和状态栏的制作。现在我除了 scipy 什么都不用了,我想让 scipy 代码有更好的函数。感谢伟大的参考“wxPython in Action”我再次学习了一些基础知识,并承诺如果我有这个冲动,我会做出非常花哨的图形用户界面!(查看示例章节,它们给出了制作对话框的整个部分,这就是我最初开始使用 wxPython 的方式)。
文件选择器对话框
我经常写简单的翻译脚本,把一些数据转换成另一种形式。我喜欢将这些用于一系列数据,并与一些不编程的同事分享。wxPython 文件选举功能来拯救。
import wx
# setup the GUI main loop
app = wx.App()
filename = wx.FileSelector()
使用这个基本代码,filename 包含所选文件的字符串路径名(可能是 unicode,这取决于您安装的 wxPython,更多关于后者)。
一些清理将包括使用启动脚本的当前目录,我们很容易做到这一点
import wx
import os
# setup the GUI main loop
app = wx.App()
filename = wx.FileSelector(default_path=os.getcwd())
如果一个人重复运行这样的脚本,在每次运行后做一些基本的清理可能是个好主意。
# ...
app.Destroy()
二十二、科学脚本
FDTD 算法在薛定谔方程中的应用
FDTD 算法在薛定谔方程中的应用
下面的代码说明了一维时域有限差分(FDTD)算法在求解简单势的一维薛定谔方程中的应用。它只需要 Numpy 和 Matplotlib。
所有的数学细节都在这个 PDF 中描述:Schrodinger_FDTD.pdf
例子
在这些图中,电势用黄色的任意单位表示,而波包的总能量用绿色的线表示,单位与电势相同。因此,虽然能量单位是而不是在左轴上,但两个能量图使用相同的单位,因此可以有效地相互比较。
根据粒子能量,黄色区域可能是经典禁止的(当绿线在黄色区域内时)。
波包从 t=0 开始,如下所示(所示阶跃电位):
在模拟的最后,它看起来像这样,取决于实际的潜在高度:
这说明了根据屏障高度,隧道穿过薄屏障。在第二种情况下,经典粒子会完全反弹,因为它的能量低于势垒:
密码
#=============================================================================
#
# Quantum Mechanical Simulation using Finite-Difference
# Time-Domain (FDTD) Method
#
# This script simulates a probability wave in the presence of multiple
# potentials. The simulation is c arried out by using the FDTD algorithm
# applied to the Schrodinger equation. The program is intended to act as
# a demonstration of the FDTD algorithm and can be used as an educational
# aid for quantum mechanics and numerical methods. The simulation
# parameters are defined in the code constants and can be freely
# manipulated to see different behaviors.
#
# NOTES
#
# The probability density plots are amplified by a factor for visual
# purposes. The psi_p quanity contains the actual probability density
# without any rescaling.
#
# BEWARE: The time step, dt, has strict requirements or else the
# simulation becomes unstable.
#
# The code has three built-in potential functions for demonstration.
#
# 1) Constant potential: Demonstrates a free particle with dispersion.
#
# 2) Step potential: Demonstrates transmission and reflection.
#
# 3) Potential barrier: Demonstrates tunneling.
#
# By tweaking the height of the potential (V0 below) as well as the
# barrier thickness (THCK below), you can see different behaviors: full
# reflection with no noticeable transmission, transmission and
# reflection, or mostly transmission with tunneling.
#
# This script requires pylab and numpy to be installed with
# Python or else it will not run.
#
#============================================================================
# Author: James Nagel <nagel@mers.byu.edu>
# 5/25/07
#
# Updates by Fernando Perez <Fernando.Perez@colorado.edu>, 7/28/07
#============================================================================
# Numerical and plotting libraries
import numpy as np
import pylab
# Set pylab to interactive mode so plots update when run outside ipython
pylab.ion()
#=============================================================================
# Utility functions
# Defines a quick Gaussian pulse function to act as an envelope to the wave
# function.
def Gaussian(x,t,sigma):
""" A Gaussian curve.
x = Variable
t = time shift
sigma = standard deviation """
return np.exp(-(x-t)**2/(2*sigma**2))
def free(npts):
"Free particle."
return np.zeros(npts)
def step(npts,v0):
"Potential step"
v = free(npts)
v[npts/2:] = v0
return v
def barrier(npts,v0,thickness):
"Barrier potential"
v = free(npts)
v[npts/2:npts/2+thickness] = v0
return v
def fillax(x,y,*args,**kw):
"""Fill the space between an array of y values and the x axis.
All args/kwargs are passed to the pylab.fill function.
Returns the value of the pylab.fill() call.
"""
xx = np.concatenate((x,np.array([x[-1],x[0]],x.dtype)))
yy = np.concatenate((y,np.zeros(2,y.dtype)))
return pylab.fill(xx, yy, *args,**kw)
#=============================================================================
#
# Simulation Constants. Be sure to include decimal points on appropriate
# variables so they become floats instead of integers.
#
N = 1200 # Number of spatial points.
T = 5*N # Number of time steps. 5*N is a nice value for terminating
# before anything reaches the boundaries.
Tp = 50 # Number of time steps to increment before updating the plot.
dx = 1.0e0 # Spatial resolution
m = 1.0e0 # Particle mass
hbar = 1.0e0 # Plank's constant
X = dx*np.linspace(0,N,N) # Spatial axis.
# Potential parameters. By playing with the type of potential and the height
# and thickness (for barriers), you'll see the various transmission/reflection
# regimes of quantum mechanical tunneling.
V0 = 1.0e-2 # Potential amplitude (used for steps and barriers)
THCK = 15 # "Thickness" of the potential barrier (if appropriate
# V-function is chosen)
# Uncomment the potential type you want to use here:
# Zero potential, packet propagates freely.
#POTENTIAL = 'free'
# Potential step. The height (V0) of the potential chosen above will determine
# the amount of reflection/transmission you'll observe
POTENTIAL = 'step'
# Potential barrier. Note that BOTH the potential height (V0) and thickness
# of the barrier (THCK) affect the amount of tunneling vs reflection you'll
# observe.
#POTENTIAL = 'barrier'
# Initial wave function constants
sigma = 40.0 # Standard deviation on the Gaussian envelope (remember Heisenberg
# uncertainty).
x0 = round(N/2) - 5*sigma # Time shift
k0 = np.pi/20 # Wavenumber (note that energy is a function of k)
# Energy for a localized gaussian wavepacket interacting with a localized
# potential (so the interaction term can be neglected by computing the energy
# integral over a region where V=0)
E = (hbar**2/2.0/m)*(k0**2+0.5/sigma**2)
#=============================================================================
# Code begins
#
# You shouldn't need to change anything below unless you want to actually play
# with the numerical algorithm or modify the plotting.
#
# Fill in the appropriate potential function (is there a Python equivalent to
# the SWITCH statement?).
if POTENTIAL=='free':
V = free(N)
elif POTENTIAL=='step':
V = step(N,V0)
elif POTENTIAL=='barrier':
V = barrier(N,V0,THCK)
else:
raise ValueError("Unrecognized potential type: %s" % POTENTIAL)
# More simulation parameters. The maximum stable time step is a function of
# the potential, V.
Vmax = V.max() # Maximum potential of the domain.
dt = hbar/(2*hbar**2/(m*dx**2)+Vmax) # Critical time step.
c1 = hbar*dt/(m*dx**2) # Constant coefficient 1.
c2 = 2*dt/hbar # Constant coefficient 2.
c2V = c2*V # pre-compute outside of update loop
# Print summary info
print 'One-dimensional Schrodinger equation - time evolution'
print 'Wavepacket energy: ',E
print 'Potential type: ',POTENTIAL
print 'Potential height V0: ',V0
print 'Barrier thickness: ',THCK
# Wave functions. Three states represent past, present, and future.
psi_r = np.zeros((3,N)) # Real
psi_i = np.zeros((3,N)) # Imaginary
psi_p = np.zeros(N,) # Observable probability (magnitude-squared
# of the complex wave function).
# Temporal indexing constants, used for accessing rows of the wavefunctions.
PA = 0 # Past
PR = 1 # Present
FU = 2 # Future
# Initialize wave function. A present-only state will "split" with half the
# wave function propagating to the left and the other half to the right.
# Including a "past" state will cause it to propagate one way.
xn = range(1,N/2)
x = X[xn]/dx # Normalized position coordinate
gg = Gaussian(x,x0,sigma)
cx = np.cos(k0*x)
sx = np.sin(k0*x)
psi_r[PR,xn] = cx*gg
psi_i[PR,xn] = sx*gg
psi_r[PA,xn] = cx*gg
psi_i[PA,xn] = sx*gg
# Initial normalization of wavefunctions
# Compute the observable probability.
psi_p = psi_r[PR]**2 + psi_i[PR]**2
# Normalize the wave functions so that the total probability in the simulation
# is equal to 1.
P = dx * psi_p.sum() # Total probability.
nrm = np.sqrt(P)
psi_r /= nrm
psi_i /= nrm
psi_p /= P
# Initialize the figure and axes.
pylab.figure()
xmin = X.min()
xmax = X.max()
ymax = 1.5*(psi_r[PR]).max()
pylab.axis([xmin,xmax,-ymax,ymax])
# Initialize the plots with their own line objects. The figures plot MUCH
# faster if you simply update the lines as opposed to redrawing the entire
# figure. For reference, include the potential function as well.
lineR, = pylab.plot(X,psi_r[PR],'b',alpha=0.7,label='Real')
lineI, = pylab.plot(X,psi_i[PR],'r',alpha=0.7,label='Imag')
lineP, = pylab.plot(X,6*psi_p,'k',label='Prob')
pylab.title('Potential height: %.2e' % V0)
# For non-zero potentials, plot them and shade the classically forbidden region
# in light red, as well as drawing a green line at the wavepacket's total
# energy, in the same units the potential is being plotted.
if Vmax !=0 :
# Scaling factor for energies, so they fit in the same plot as the
# wavefunctions
Efac = ymax/2.0/Vmax
V_plot = V*Efac
pylab.plot(X,V_plot,':k',zorder=0) # Potential line.
fillax(X,V_plot, facecolor='y', alpha=0.2,zorder=0)
# Plot the wavefunction energy, in the same scale as the potential
pylab.axhline(E*Efac,color='g',label='Energy',zorder=1)
pylab.legend(loc='lower right')
pylab.draw()
# I think there's a problem with pylab, because it resets the xlim after
# plotting the E line. Fix it back manually.
pylab.xlim(xmin,xmax)
# Direct index assignment is MUCH faster than using a spatial FOR loop, so
# these constants are used in the update equations. Remember that Python uses
# zero-based indexing.
IDX1 = range(1,N-1) # psi [ k ]
IDX2 = range(2,N) # psi [ k + 1 ]
IDX3 = range(0,N-2) # psi [ k - 1 ]
for t in range(T+1):
# Precompute a couple of indexing constants, this speeds up the computation
psi_rPR = psi_r[PR]
psi_iPR = psi_i[PR]
# Apply the update equations.
psi_i[FU,IDX1] = psi_i[PA,IDX1] + \
c1*(psi_rPR[IDX2] - 2*psi_rPR[IDX1] +
psi_rPR[IDX3])
psi_i[FU] -= c2V*psi_r[PR]
psi_r[FU,IDX1] = psi_r[PA,IDX1] - \
c1*(psi_iPR[IDX2] - 2*psi_iPR[IDX1] +
psi_iPR[IDX3])
psi_r[FU] += c2V*psi_i[PR]
# Increment the time steps. PR -> PA and FU -> PR
psi_r[PA] = psi_rPR
psi_r[PR] = psi_r[FU]
psi_i[PA] = psi_iPR
psi_i[PR] = psi_i[FU]
# Only plot after a few iterations to make the simulation run faster.
if t % Tp == 0:
# Compute observable probability for the plot.
psi_p = psi_r[PR]**2 + psi_i[PR]**2
# Update the plots.
lineR.set_ydata(psi_r[PR])
lineI.set_ydata(psi_i[PR])
# Note: we plot the probability density amplified by a factor so it's a
# bit easier to see.
lineP.set_ydata(6*psi_p)
pylab.draw()
# So the windows don't auto-close at the end if run outside ipython
pylab.ioff()
pylab.show()
One-dimensional Schrodinger equation - time evolution
Wavepacket energy: 0.0124932555014
Potential type: step
Potential height V0: 0.01
Barrier thickness: 15
附件
Schrodinger_FDTD.pdf
schrod_barrier_hi_sm2.png
schrod_barrier_lo_sm2.png
schrod_step_hi_sm2.png
schrod_step_init_sm2.png
schrod_step_lo_sm2.png
将 NumPy 与其他语言一起使用(高级)
将 NumPy 与其他语言一起使用(高级)
扩展
扩展
骨骼
extmodule.h
:
#ifndef EXTMODULE_H
#define EXTMODULE_H
#ifdef __cplusplus
extern “C” {
#endif
/* Python.h must be included before everything else */
#include “Python.h”
/* include system headers here */
#if !defined(EXTMODULE_IMPORT_ARRAY)
#define NO_IMPORT_ARRAY
#endif
#include “numpy/arrayobject.h”
#ifdef __cplusplus
}
#endif
#endif
注意PY_ARRAY_UNIQUE_SYMBOL
必须为每个扩展模块设置一个唯一的值。但是,您实际上根本不需要设置它,除非您要编译一个使用两个不同源文件的扩展模块
extmodule.c
:
#define EXTMODULE_IMPORT_ARRAY
#include “extmodule.h”
#undef EXTMODULE_IMPORT_ARRAY
static PyObject* FooError;
static PyObject* Ext_Foo(PyObject* obj, PyObject* args) {
Py_INCREF(Py_None); return Py_None;
}
static PyMethodDef methods[] = {
{“foo”, (PyCFunction) Ext_Foo, METH_VARARGS, “”}, {NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC init_extmodule() {
PyObject* m;
m = Py_InitModule(“_extmodule”, methods);
import_array();
SVMError = PyErr_NewException(“_extmodule.FooError”, NULL, NULL);
Py_INCREF(FooError);
PyModule_AddObject(m, “FooError”, FooError);
}
如果您的扩展模块包含在一个单独的源文件中,那么您可以完全去掉 extmodule.h,并用
#inlude “Python.h”
#include “numpy/arrayobject.h”
/* remainder of extmodule.c after here */
在 Windows 上调试 C 扩展
在 Windows 上调试 C 扩展可能很棘手。如果在调试模式下编译扩展代码,必须链接 Python 库的调试版本,例如Python24_d.lib
。使用 Visual Studio 构建时,这由Python24.h
中的一个实用程序处理。如果您强制编译器将调试代码链接到发布库,您可能会得到以下错误(尤其是在编译 SWIG 包装的代码时):
extmodule.obj : error LNK2019: unresolved external symbol __imp___Py_Dealloc referenced in function _PySwigObject_format
extmodule.obj : error LNK2019: unresolved external symbol __imp___Py_NegativeRefcount referenced in function _PySwigObject_format
extmodule.obj : error LNK2001: unresolved external symbol __imp___Py_RefTotal
extmodule.obj : error LNK2019: unresolved external symbol __imp___PyObject_DebugFree referenced in function _PySwigObject_dealloc
extmodule.obj : error LNK2019: unresolved external symbol __imp___PyObject_DebugMalloc referenced in function _PySwigObject_New
extmodule.obj : error LNK2019: unresolved external symbol __imp__Py_InitModule4TraceRefs referenced in function _init_extmodule
但是,如果您想导入这个可调试的扩展模块,现在还需要 Python 解释器的调试版本。现在,您还需要调试您使用的每一个其他扩展模块。显然,这可能需要一些时间来解决。
另一种方法是将库代码构建为调试 DLL。这样,您至少可以确保您的扩展模块将正确的数据传递给您包装的库代码。
另外,MinGW GCC 编译器似乎没有生成 Visual Studio 调试器能够理解的调试符号。
瓦尔格林德
为了开发一个稳定的扩展模块,检查由 C 代码完成的内存分配和内存访问是非常重要的。在 Linux 上,可以使用 Valgrind 。在 Windows 上,您可以尝试商业工具,如 Rational PurifyPlus 。
在使用 Valgrind 之前,请确保您的扩展模块是使用-g
开关编译到 GCC 的,以便在检测到错误时可以获得有用的堆栈跟踪。
然后将以下内容放入一个 shell 脚本中,说valgrind_py.sh
:
#!/bin/sh valgrind
–tool=memcheck –leak-check=yes –error-limit=no –suppressions=valgrind-python.supp –num-callers=10 -v python $1
valgrind-python.supp
抑制 Python 代码引起的一些警告。您可以在 Python SVN 存储库中找到 Python 2.4 的抑制文件。另请参见同一位置的自述文件。默认情况下,某些抑制会被注释掉。通过删除#注释标记来启用它们。
执行chmod +x valgrind_py.sh
并作为./valgrind_py.sh test_extmodule.py
运行。
证明文件
- 扩展和嵌入 Python 解释器(先看这个)
- Python/C API 参考手册(然后浏览此处)
- NumPy 指南第 13 章描述了完整的 API
- 第 14 章涉及延伸!NumPy(确保您有日期为 2006 年 3 月 15 日或更晚的版本)
例子
- SciPy SVN 存储库中的图像
NumPy arrays
(小例子集锦)
邮件列表线程
ctypes(类型)
ctypes(类型)
介绍
ctypes 是 Python 2.3 及更高版本的高级外来函数接口包。它包含在 Python 2.5 的标准库中。
ctypes 允许调用从 DLLs 共享库公开的函数,并拥有广泛的设施来创建、访问和操作 Python 中简单和复杂的 C 数据类型——换句话说:用纯 Python 包装库。甚至可以用纯 Python 实现 C 回调函数。
ctypes 还包括一个代码生成器工具链,允许从 C 头文件自动创建库包装器。ctypes 适用于 Windows、Mac OS X、Linux、Solaris、FreeBSD、OpenBSD 等系统。
确保您至少有 ctypes 1 . 0 . 1 或更高版本。
用 python 调用或运行 C 代码的其他可能性包括: SWIG 、 Cython 、 Weave 、 cffi 等。
ctypes 入门
Python 的 ctypes 教程和 ctypes 文档提供了大量关于 ctypes 入门的信息。
假设您已经构建了一个名为foo.dll
或libfoo.so
的库,其中包含一个名为bar
的函数,该函数将一个指向双精度缓冲区的指针和一个 int 作为参数并返回一个 int,下面的代码应该可以让您开始运行。以下几节涵盖了一些可能的构建脚本、C 代码和 Python 代码。
如果你想用 distutils 构建你的 DLL/共享库,看看! OOF2 中包含的共享数据库扩展。这可能会在某个时候包含在 numpy.distutils 中。
Nmake Makefile (Windows)
在 Visual Studio 命令提示符下运行 nmake,使用以下文件进行生成。
您应该能够使用任何版本的 Visual Studio 编译器来构建 DLL,而不管用于编译 Python 的编译器是什么。请记住,您不应该在不同的调试/发布和单线程/多线程运行时之间分配/释放内存,也不应该在不同运行时的文件上操作。
CXX = cl.exe
LINK = link.exe
CPPFLAGS = -D_WIN32 -D_USRDLL -DFOO_DLL -DFOO_EXPORTS
CXXFLAGSALL = -nologo -EHsc -GS -W3 -Wp64 $(CPPFLAGS)
CXXFLAGSDBG = -MDd -Od -Z7 -RTCcsu
CXXFLAGSOPT = -MD -O2
#CXXFLAGS = $(CXXFLAGSALL) $(CXXFLAGSDBG)
CXXFLAGS = $(CXXFLAGSALL) $(CXXFLAGSOPT)
LINKFLAGSALL = /nologo /DLL
LINKFLAGSDBG = /DEBUG
LINKFLAGSOPT =
#LINKFLAGS = $(LINKFLAGSALL) $(LINKFLAGSDBG)
LINKFLAGS = $(LINKFLAGSALL) $(LINKFLAGSOPT)
all: foo.dll
foo.dll: foo.obj
$(LINK) $(LINKFLAGS) foo.obj /OUT:foo.dll
svm.obj: svm.cpp svm.h
$(CXX) $(CXXFLAGS) -c foo.cpp
clean:
-erase /Q *.obj *.dll *.exp *.lib
建筑
您可以将以下文件与 SCons 一起使用来构建共享库。
env = Environment()
env.Replace(CFLAGS=['-O2','-Wall','-ansi','-pedantic'])
env.SharedLibrary('foo', ['foo.cpp'])
foo.cpp
#include <stdio.h>
#ifdef FOO_DLL
#ifdef FOO_EXPORTS
#define FOO_API __declspec(dllexport)
#else
#define FOO_API __declspec(dllimport)
#endif /* FOO_EXPORTS */
#else
#define FOO_API extern /* XXX confirm this */
#endif /* FOO_DLL */
#ifdef __cplusplus
extern "C" {
#endif
extern FOO_API int bar(double* data, int len) {
int i;
printf("data = %p\n", (void*) data);
for (i = 0; i < len; i++) {
printf("data[%d] = %f\n", i, data[i]);
}
printf("len = %d\n", len);
return len + 1;
}
#ifdef __cplusplus
}
#endif
在 Windows 上为 foo 构建 DLL 时,定义FOO_DLL
和FOO_EXPORTS
(这是您在构建与 ctypes 一起使用的 DLL 时想要做的)。链接动态链接库时,定义FOO_DLL
。当链接包含 foo 的静态库时,或者当在可执行文件中包含 foo 时,不要定义任何东西。
如果你不清楚是为了什么,请阅读 c++ d open 迷你 HOWTO 的部分 3。这允许您在一堆 C++类之上编写带有 C 链接的函数包装器,以便您可以将它们与 ctypes 一起使用。或者,您可能更喜欢编写 C 代码。
foo.py
import numpy as N
import ctypes as C
_foo = N.ctypeslib.load_library('libfoo', '.')
_foo.bar.restype = C.c_int
_foo.bar.argtypes = [C.POINTER(C.c_double), C.c_int]
def bar(x):
return _foo.bar(x.ctypes.data_as(C.POINTER(C.c_double)), len(x))
x = N.random.randn(10)
n = bar(x)
NumPy 数组的 ctypes 属性
最近向 NumPy 数组添加了 ctypes 属性:
In [18]: x = N.random.randn(2,3,4)
In [19]: x.ctypes.data
Out[19]: c_void_p(14394256)
In [21]: x.ctypes.data_as(ctypes.POINTER(c_double))
In [24]: x.ctypes.shape
Out[24]: <ctypes._endian.c_long_Array_3 object at 0x00DEF2B0>
In [25]: x.ctypes.shape[:3]
Out[25]: [2, 3, 4]
In [26]: x.ctypes.strides
Out[26]: <ctypes._endian.c_long_Array_3 object at 0x00DEF300>
In [27]: x.ctypes.strides[:3]
Out[27]: [96, 32, 8]
一般来说,C 函数可能会取一个指向数组数据的指针、一个指示数组维数的整数(在这里传递 ndim 属性的值)和两个指向形状和跨距信息的 int 指针。
如果你的 C 函数假定存储是连续的,你可能想用一个 Python 函数包装它,这个函数调用!所有输入数组上的 NumPyascontiguousarray
功能。
带有 ctypes 参数类型的 NumPy 的 ndpointer
从 ctypes 0.9.9.9 开始,任何实现 from_param 方法的类都可以在函数的 argtypes 列表中使用。在 ctypes 调用 C 函数之前,它使用 argtypes 列表来检查每个参数。
使用!NumPy 的 ndpointer 函数,可以构造一些非常有用的 argtypes 类,例如:
from numpy.ctypeslib import ndpointer
arg1 = ndpointer(dtype='<f4')
arg2 = ndpointer(ndim=2)
arg3 = ndpointer(shape=(10,10))
arg4 = ndpointer(flags='CONTIGUOUS,ALIGNED')
# or any combination of the above
arg5 = ndpointer(dtype='>i4', flags='CONTIGUOUS')
func.argtypes = [arg1,arg2,arg3,arg4,arg5]
现在,如果一个论点不符合要求,a!引发类型错误。这使得人们可以确保传递给 C 函数的数组是该函数可以处理的形式。
另请参见 ctypes 和 ndpointer 上的邮件列表线程。
通过回调动态分配
ctypes 支持回调的思想,允许 C 代码通过函数指针回调到 Python 中。这是可能的,因为 ctypes 在调用 C 函数之前会释放 Python 全局解释器锁(GIL)。
我们可以用这个功能来分配!NumPy 数组,如果我们需要一个 C 代码操作的缓冲区。这可以避免在某些情况下必须复制数据。您也不必担心在完成后释放 C 数据。通过将缓冲区分配为!NumPy 数组,Python 垃圾收集器可以解决这个问题。
Python 代码:
from ctypes import *
ALLOCATOR = CFUNCTYPE(c_long, c_int, POINTER(c_int))
# load your library as lib
lib.baz.restype = None
lib.baz.argtypes = [c_float, c_int, ALLOCATOR]
这并不是定义分配器最漂亮的方式(我也不确定 c_long 是否是正确的返回类型),但是 ctype 中有一些 bug 似乎使这成为目前唯一的方式。最终,我们希望像这样编写分配器(但它还不起作用):
from numpy.ctypeslib import ndpointer
ALLOCATOR = CFUNCTYPE(ndpointer('f4'), c_int, POINTER(c_int))
以下情况似乎也会引起问题:
ALLOCATOR = CFUNCTYPE(POINTER(c_float), c_int, POINTER(c_int))
ALLOCATOR = CFUNCTYPE(c_void_p, c_int, POINTER(c_int))
ALLOCATOR = CFUNCTYPE(None, c_int, POINTER(c_int), POINTER(c_void_p))
可能的故障包括!引发系统错误异常,解释器崩溃或解释器挂起。查看这些邮件列表线程了解更多详细信息: 在回调中赋值时指针指针不变 使用回调返回指针(c_float) * 使用回调函数和 as_parameter 使用 NumPy ndpointer
举个例子。示例的 C 代码:
#ifndef CSPKREC_H
#define CSPKREC_H
#ifdef FOO_DLL
#ifdef FOO_EXPORTS
#define FOO_API __declspec(dllexport)
#else
#define FOO_API __declspec(dllimport)
#endif
#else
#define FOO_API
#endif
#endif
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void*(*allocator_t)(int, int*);
extern FOO_API void foo(allocator_t allocator) {
int dim = 2;
int shape[] = {2, 3};
float* data = NULL;
int i, j;
printf("foo calling allocator\n");
data = (float*) allocator(dim, shape);
printf("allocator returned in foo\n");
printf("data = 0x%p\n", data);
for (i = 0; i < shape[0]; i++) {
for (j = 0; j < shape[1]; j++) {
*data++ = (i + 1) * (j + 1);
}
}
}
#ifdef __cplusplus
}
#endif
如果您是 C 或 C++中函数指针的新手,请查看函数指针教程。和 Python 代码:
from ctypes import *
import numpy as N
allocated_arrays = []
def allocate(dim, shape):
print 'allocate called'
x = N.zeros(shape[:dim], 'f4')
allocated_arrays.append(x)
ptr = x.ctypes.data_as(c_void_p).value
print hex(ptr)
print 'allocate returning'
return ptr
lib = cdll['callback.dll']
lib.foo.restype = None
ALLOCATOR = CFUNCTYPE(c_long, c_int, POINTER(c_int))
lib.foo.argtypes = [ALLOCATOR]
print 'calling foo'
lib.foo(ALLOCATOR(allocate))
print 'foo returned'
print allocated_arrays[0]
分配函数创建一个新的!NumPy 数组,并将其放在列表中,以便我们在回调函数返回后保留对它的引用。预期产出:
calling foo
foo calling allocator
allocate called
0xaf5778
allocate returning
allocator returned in foo
data = 0x00AF5778
foo returned
[[ 1\. 2\. 3.]
[ 2\. 4\. 6.]]
这是分配器类管理这类事情的另一个想法。除了维度和形状之外,这个分配器函数还接受一个字符来指示要分配什么类型的数组。您可以在NPY_TYPECHAR
枚举中从 ndarrayobject.h 头中获取这些类型代码。
from ctypes import *
import numpy as N
class Allocator:
CFUNCTYPE = CFUNCTYPE(c_long, c_int, POINTER(c_int), c_char)
def __init__(self):
self.allocated_arrays = []
def __call__(self, dims, shape, dtype):
x = N.empty(shape[:dims], N.dtype(dtype))
self.allocated_arrays.append(x)
return x.ctypes.data_as(c_void_p).value
def getcfunc(self):
return self.CFUNCTYPE(self)
cfunc = property(getcfunc)
在 Python 中这样使用它:
lib.func.argtypes = [..., Allocator.CFUNCTYPE]
def func():
alloc = Allocator()
lib.func(..., alloc.cfunc)
return tuple(alloc.allocated_arrays[:3])
对应的 C 代码:
typedef void*(*allocator_t)(int, int*, char);
void func(..., allocator_t allocator) {
/* ... */
int dims[] = {2, 3, 4};
double* data = (double*) allocator(3, dims, 'd');
/* allocate more arrays here */
}
上面介绍的分配器都不是线程安全的。如果您有多个 Python 线程调用调用回调的 C 代码,您将不得不做一些更聪明的事情。
更有用的代码片段
假设您有一个类似下面这样的 C 函数,它对指针对指针的数据结构进行操作。
void foo(float** data, int len) {
float** x = data;
for (int i = 0; i < len; i++, x++) {
/* do something with *x */
}
}
您可以从现有的二维创建必要的结构!使用以下代码的 NumPy 数组:
x = N.array([[10,20,30], [40,50,60], [80,90,100]], 'f4')
f4ptr = POINTER(c_float)
data = (f4ptr*len(x))(*[row.ctypes.data_as(f4ptr) for row in x])
f4ptr*len(x)
创建一个 ctypes 数组类型,该类型足够大,可以包含指向数组中每一行的指针。
异构类型示例
这里有一个使用异构数据类型(记录数组)的简单例子。
但是,要注意的是,C 中的 NumPy 重数组和相应的结构可能不 是全等的。
此外,跨平台的结构不是标准化的...换句话说,' '注意填充问题!' '
样本 c
#include <stdio.h>
typedef struct Weather_t {
int timestamp;
char desc[12];
} Weather;
void print_weather(Weather* w, int nelems)
{
int i;
for (i=0;i<nelems;++i) {
printf("timestamp: %d\ndescription: %s\n\n", w[i].timestamp, w[i].desc);
}
}
结构
env = Environment()
env.Replace(CFLAGS=['-O2','-Wall','-ansi','-pedantic'])
env.SharedLibrary('sample', ['sample.c'])
sample.py
import numpy as N
import ctypes as C
dat = [[1126877361,'sunny'], [1126877371,'rain'], [1126877385,'damn nasty'], [1126877387,'sunny']]
dat_dtype = N.dtype([('timestamp','i4'),('desc','|S12')])
arr = N.rec.fromrecords(dat,dtype=dat_dtype)
_sample = N.ctypeslib.load_library('libsample','.')
_sample.print_weather.restype = None
_sample.print_weather.argtypes = [N.ctypeslib.ndpointer(dat_dtype, flags='aligned, contiguous'), C.c_int]
def print_weather(x):
_sample.print_weather(x, x.size)
if __name__=='__main__':
print_weather(arr)
斐波那契示例(使用 NumPy 数组、C 和 Scons)
以下内容在 Windows(使用 MinGW)和 GNU/Linux 32 位操作系统(最近一次测试是 2009 年 8 月 13 日)上进行了测试和工作。将所有三个文件复制到同一个目录。
C 代码(递归计算斐波那契数):
/*
Filename: fibonacci.c
To be used with fibonacci.py, as an imported library. Use Scons to compile,
simply type 'scons' in the same directory as this file (see www.scons.org).
*/
/* Function prototypes */
int fib(int a);
void fibseries(int *a, int elements, int *series);
void fibmatrix(int *a, int rows, int columns, int *matrix);
int fib(int a)
{
if (a <= 0) /* Error -- wrong input will return -1\. */
return -1;
else if (a==1)
return 0;
else if ((a==2)||(a==3))
return 1;
else
return fib(a - 2) + fib(a - 1);
}
void fibseries(int *a, int elements, int *series)
{
int i;
for (i=0; i < elements; i++)
{
series[i] = fib(a[i]);
}
}
void fibmatrix(int *a, int rows, int columns, int *matrix)
{
int i, j;
for (i=0; i<rows; i++)
for (j=0; j<columns; j++)
{
matrix[i * columns + j] = fib(a[i * columns + j]);
}
}
Python 代码:
"""
Filename: fibonacci.py
Demonstrates the use of ctypes with three functions:
(1) fib(a)
(2) fibseries(b)
(3) fibmatrix(c)
"""
import numpy as nm
import ctypes as ct
# Load the library as _libfibonacci.
# Why the underscore (_) in front of _libfibonacci below?
# To mimimise namespace pollution -- see PEP 8 (www.python.org).
_libfibonacci = nm.ctypeslib.load_library('libfibonacci', '.')
_libfibonacci.fib.argtypes = [ct.c_int] # Declare arg type, same below.
_libfibonacci.fib.restype = ct.c_int # Declare result type, same below.
_libfibonacci.fibseries.argtypes = [nm.ctypeslib.ndpointer(dtype = nm.int),\
ct.c_int,\
nm.ctypeslib.ndpointer(dtype = nm.int)]
_libfibonacci.fibseries.restype = ct.c_void_p
_libfibonacci.fibmatrix.argtypes = [nm.ctypeslib.ndpointer(dtype = nm.int),\
ct.c_int, ct.c_int,\
nm.ctypeslib.ndpointer(dtype = nm.int)]
_libfibonacci.fibmatrix.restype = ct.c_void_p
def fib(a):
"""Compute the n'th Fibonacci number.
ARGUMENT(S):
An integer.
RESULT(S):
The n'th Fibonacci number.
EXAMPLE(S):
>>> fib(8)
13
>>> fib(23)
17711
>>> fib(0)
-1
"""
return _libfibonacci.fib(int(a))
def fibseries(b):
"""Compute an array containing the n'th Fibonacci number of each entry.
ARGUMENT(S):
A list or NumPy array (dim = 1) of integers.
RESULT(S):
NumPy array containing the n'th Fibonacci number of each entry.
EXAMPLE(S):
>>> fibseries([1,2,3,4,5,6,7,8])
array([ 0, 1, 1, 2, 3, 5, 8, 13])
>>> fibseries(range(1,12))
array([ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55])
"""
b = nm.asarray(b, dtype=nm.intc)
result = nm.empty(len(b), dtype=nm.intc)
_libfibonacci.fibseries(b, len(b), result)
return result
def fibmatrix(c):
"""Compute a matrix containing the n'th Fibonacci number of each entry.
ARGUMENT(S):
A nested list or NumPy array (dim = 2) of integers.
RESULT(S):
NumPy array containing the n'th Fibonacci number of each entry.
EXAMPLE(S):
>>> from numpy import array
>>> fibmatrix([[3,4],[5,6]])
array([[1, 2],
[3, 5]])
>>> fibmatrix(array([[1,2,3],[4,5,6],[7,8,9]]))
array([[ 0, 1, 1],
[ 2, 3, 5],
[ 8, 13, 21]])
"""
tmp = nm.asarray(c)
rows, cols = tmp.shape
c = tmp.astype(nm.intc)
result = nm.empty(c.shape, dtype=nm.intc)
_libfibonacci.fibmatrix(c, rows, cols, result)
return result
下面是结构文件内容(文件名:结构):
env = Environment()
env.Replace(CFLAGS=['-O2', '-Wall', '-ansi', '-pedantic'])
env.SharedLibrary('libfibonacci', ['fibonacci.c'])
在 Python 解释器(或您使用的任何解释器)中,执行以下操作:
>>> import fibonacci as fb
>>> fb.fib(8)
13
>>> fb.fibseries([5,13,2,6]
array([ 3, 144, 1, 5])
相关邮件列表线索
ctypes-user 邮件列表中的一些有用的线索:
- [http://aspn . activestate . com/ASPN/Mail/Message/ctypes-users/3119087对指针(POINTER(ctype))进行索引时出现索引错误]
- [http://aspn . activestate . com/ASPN/Mail/Message/ctypes-users/3118513向 NumPy 添加 ctypes 支持]
- [http://aspn . activestate . com/ASPN/Mail/Message/ctypes-users/3118656确定 ctype 是否为指针类型(曾为 RE:向 NumPy 添加 ctype 支持)】
- [http://aspn . activestate . com/ASPN/Mail/Message/ctypes-users/3117306检查无值的空指针错误]
- 【http://aspn . activestate . com/ASPN/Mail/Message/ctypes-users/3205951从 C 到 Python 的回调问题】
- [http://thread . gmane . org/gmane . comp . python . numeric . general/7418ctypes 和 ndpointer]
- [http://thread.gmane.org/gmane.comp.python.ctypes/311664 符号整数问题]
托马斯·海勒的回答特别有见地。
证明文件
- 【http://starship . python . net/crew/theller/ctypes/tutorial . htmlctypes tutorial】
- [http://docs.python.org/dev/lib/module-ctypes.html13.14 ctypes–Python 的一个外来函数库。]
F2py
F2py
本页提供了如何使用 F2py fortran 包装程序的示例。
可以从简单的例程开始,甚至可以包装完整的 Fortran 模块。 F2py 用在 SciPy 本身,你可以在 SciPy 的源代码中找到一些例子。
简短的例子
从 lapack 包装函数
摘自 ArndBaecker 于 2006-06-22 给 scipy 用户的消息
多亏了 f2py,在许多情况下包装 Fortran 代码(稍加努力)是微不足道的。对于需要很多参数的复杂函数,包装器会变得很长。幸运的是,看scipy/Lib/linalg/generic_flapack.pyf
可以学到很多东西,尤其是http://cens.ioc.ee/projects/f2py2e/的文档非常棒。我也发现 FernandoPerez 的 f2py 笔记很有帮助
让我试着对如何开始给出一些一般性的评论(这一切的真正权威当然是 Pearu,所以如果我在这里弄错了,请纠正我):首先找到一个例程,它将完成您想要的工作:如果 lapack 文档在 Linux 上安装正确,您可以做http://www.netlib.org/提供了一个很好的决策树确保它不存在于 scipy 中:
from scipy.lib import lapack
lapack.clapack.<TAB> (assuming Ipython)
lapack.clapack.<routine_name>
Remark: routines starting with c/z are for double/single complex`` and routines for d/s for double/single real numbers.`` The calling sequence for c/z and d/s are (I think always) the same and`` sometimes they are also the same for the real and complex case.``* Then one has to download the fortran file for the lapack routine of interest.``* Generate wrapper by calling``* Generate library
* You can use this by
import wrap_lap
Note, that this is not yet polished (this is the part on`` which has to spent some effort ;-) ), i.e. one`` has to tell which variables are input, which are output`` and which are optional. In addition temporary`` storage has to be provided with the right dimensions`` as described in the documentation part of the lapack routine.
具体(非常简单)的例子(非 lapack):
包装埃尔米特多项式
下载代码(在几个小时的谷歌搜索后找到;-),来自http://CDM . unimo . it/home/mate matica/funa ro . daniele/splib . txt
和提取
生成包装框架:
# only run the following line _once_
# (and never again, otherwise the hand-modified hermite.pyf
# goes down the drains)
f2py -m hermite -h hermite.pyf hermite.f
然后修改
创建模块:
f2py -c hermite.pyf hermite.f
# add this if you want:
-DF2PY_REPORT_ON_ARRAY_COPY=1 -DF2PY_REPORT_ATEXIT
简单测试:
import hermite
hermite.vahepo(2,2.0)
import scipy
scipy.special.hermite(2)(2.0)
关于如何包装带状矩阵的例程的一个更复杂的例子可以在http://www.physik.tu-dresden.de/~baecker/comp_talks.html的“Python 和 Co -一些最近的发展”中找到。
包装一个简单的 C 代码
f2py 也能够处理 C 代码。维基上有一个例子:[“Cookbook/f2py _ and _ NumPy”]。
简单数字代码的逐步包装:冰原模拟交互系统
http://websrv.cs.umt.edu/isis/index.php/F2py_example
带有基本数组转换的直列编织(无闪电战)
带有基本数组转换的直列编织(无闪电战)
Python 和 Numpy 旨在表达对许多大小的传入数据透明工作的通用语句。在闪电战转换中使用内联编织可以显著加快许多数值运算(例如,添加一系列数组),因为在某些方面它绕过了通用性。如何在保持通用性的同时,用内联 C 代码加速算法?Numpy 提供的一个工具是迭代器。因为迭代器为您跟踪内存索引,所以它的操作非常类似于 Python 本身的迭代概念。你可以用 C 语言编写一个循环,简单地说“获取一个串行对象中的下一个元素——the!PyArrayObject–对其进行操作,直到不再有元素为止。”
这是多维迭代器的一个非常简单的例子,以及它们“广播”兼容形状数组的能力。它表明,完全不知道维度的同一代码可以根据广播规则实现完全不同的计算。在这种情况下,我假设 a 的维度至少和 b 一样多。重要的是要知道 a 的编织数组转换可以让你在 C++中访问到:py _ a–!py objecta _ array–!PyArrayObjecta –( c 型 ) py_array- >数据
import numpy as npy
from scipy.weave import inline
def multi_iter_example():
a = npy.ones((4,4), npy.float64)
# for the sake of driving home the "dynamic code" approach...
dtype2ctype = {
npy.dtype(npy.float64): 'double',
npy.dtype(npy.float32): 'float',
npy.dtype(npy.int32): 'int',
npy.dtype(npy.int16): 'short',
}
dt = dtype2ctype.get(a.dtype)
# this code does a = a*b inplace, broadcasting b to fit the shape of a
code = \
"""
%s *p1, *p2;
PyObject *itr;
itr = PyArray_MultiIterNew(2, a_array, b_array);
while(PyArray_MultiIter_NOTDONE(itr)) {
p1 = (%s *) PyArray_MultiIter_DATA(itr, 0);
p2 = (%s *) PyArray_MultiIter_DATA(itr, 1);
*p1 = (*p1) * (*p2);
PyArray_MultiIter_NEXT(itr);
}
""" % (dt, dt, dt)
b = npy.arange(4, dtype=a.dtype)
print '\n A B '
print a, b
# this reshaping is redundant, it would be the default broadcast
b.shape = (1,4)
inline(code, ['a', 'b'])
print "\ninline version of a*b[None,:],"
print a
a = npy.ones((4,4), npy.float64)
b = npy.arange(4, dtype=a.dtype)
b.shape = (4,1)
inline(code, ['a', 'b'])
print "\ninline version of a*b[:,None],"
print a
在 iterators_example.py
和 iterators.py
中还有另外两个迭代器应用。
深入“内联”方法
内联的文档字符串非常庞大,表明在集成内联代码时支持各种编译选项。我利用这一点让一些专门的 FFTW 调用变得简单多了,并且只在几行代码中增加了对内联快速傅立叶变换的支持。在这个例子中,我读入了一个纯 C 代码的文件,并在我的内联语句中将其用作 support_code 。我还使用了一个来自 Numpy distutils 的工具来定位我的 FFTW 库和头文件。
import numpy as N
from scipy.weave import inline
from os.path import join, split
from numpy.distutils.system_info import get_info
fft1_code = \
"""
char *i, *o;
i = (char *) a;
o = inplace ? i : (char *) b;
if(isfloat) {
cfft1d(reinterpret_cast<fftwf_complex*>(i),
reinterpret_cast<fftwf_complex*>(o),
xdim, len_array, direction, shift);
} else {
zfft1d(reinterpret_cast<fftw_complex*>(i),
reinterpret_cast<fftw_complex*>(o),
xdim, len_array, direction, shift);
}
"""
extra_code = open(join(split(__file__)[0],'src/cmplx_fft.c')).read()
fftw_info = get_info('fftw3')
def fft1(a, shift=True, inplace=False):
if inplace:
_fft1_work(a, -1, shift, inplace)
else:
return _fft1_work(a, -1, shift, inplace)
def ifft1(a, shift=True, inplace=False):
if inplace:
_fft1_work(a, +1, shift, inplace)
else:
return _fft1_work(a, +1, shift, inplace)
def _fft1_work(a, direction, shift, inplace):
# to get correct C-code, b always must be an array (but if it's
# not being used, it can be trivially small)
b = N.empty_like(a) if not inplace else N.array([1j], a.dtype)
inplace = 1 if inplace else 0
shift = 1 if shift else 0
isfloat = 1 if a.dtype.itemsize==8 else 0
len_array = N.product(a.shape)
xdim = a.shape[-1]
inline(fft1_code, ['a', 'b', 'isfloat', 'inplace',
'len_array', 'xdim', 'direction', 'shift'],
support_code=extra_code,
headers=['<fftw3.h>'],
libraries=['fftw3', 'fftw3f'],
include_dirs=fftw_info['include_dirs'],
library_dirs=fftw_info['library_dirs'],
compiler='gcc')
if not inplace:
return b
该代码可在[附件:fftmod.tar.gz fftmod.tar.gz]中找到。
附件
Pyrex 和 NumPy
Pyrex 和 NumPy
请注意,这里描述的代码有点过时,因为今天cynthon是 Pyrex 的主动维护版本,numpy 现在附带了 cynthon 示例。
我们不会同时维护维基和源代码目录,而是继续更新源代码,将保留在这里。
旧 Pyrex 页面
Pyrex 是一种为 Python 编写 C 扩展的语言。它的语法与编写 Python 非常相似。一个文件被编译成一个文件,然后像 Python 的标准 C 扩展模块一样被编译。许多人发现用 Pyrex 编写扩展模块比用 C 语言或使用其他工具(如 SWIG)更可取。
这个页面是用 Pyrex 访问 numpy 数组的起点。请注意,在 NumPy (SVN)的当前版本中,该目录包含一个完整的工作示例,该示例包含本页中的代码,还包含一个适当的文件,因此您可以使用标准 Python 机制安装它。这应该有助于你快速起床和跑步。
这里有一个我称之为“c_python.pxd”的文件:
cdef extern from "Python.h":
ctypedef int Py_intptr_t
这是“c_numpy.pxd”:
cimport c_python
cdef extern from "numpy/arrayobject.h":
ctypedef class numpy.ndarray [object PyArrayObject]:
cdef char *data
cdef int nd
cdef c_python.Py_intptr_t *dimensions
cdef c_python.Py_intptr_t *strides
cdef object base
# descr not implemented yet here...
cdef int flags
cdef int itemsize
cdef object weakreflist
cdef void import_array()
这里有一个示例程序,将它命名为类似“test.pyx”的后缀。
cimport c_numpy
cimport c_python
import numpy
c_numpy.import_array()
def print_array_info(c_numpy.ndarray arr):
cdef int i
print '-='*10
print 'printing array info for ndarray at 0x%0lx'%(<c_python.Py_intptr_t>arr,)
print 'print number of dimensions:',arr.nd
print 'address of strides: 0x%0lx'%(<c_python.Py_intptr_t>arr.strides,)
print 'strides:'
for i from 0<=i<arr.nd:
# print each stride
print ' stride %d:'%i,<c_python.Py_intptr_t>arr.strides[i]
print 'memory dump:'
print_elements( arr.data, arr.strides, arr.dimensions, arr.nd, sizeof(double), arr.dtype )
print '-='*10
print
cdef print_elements(char *data,
c_python.Py_intptr_t* strides,
c_python.Py_intptr_t* dimensions,
int nd,
int elsize,
object dtype):
cdef c_python.Py_intptr_t i,j
cdef void* elptr
if dtype not in [numpy.dtype(numpy.object_),
numpy.dtype(numpy.float64)]:
print ' print_elements() not (yet) implemented for dtype %s'%dtype.name
return
if nd ==0:
if dtype==numpy.dtype(numpy.object_):
elptr = (<void**>data)[0] #[0] dereferences pointer in Pyrex
print ' ',<object>elptr
elif dtype==numpy.dtype(numpy.float64):
print ' ',(<double*>data)[0]
elif nd == 1:
for i from 0<=i<dimensions[0]:
if dtype==numpy.dtype(numpy.object_):
elptr = (<void**>data)[0]
print ' ',<object>elptr
elif dtype==numpy.dtype(numpy.float64):
print ' ',(<double*>data)[0]
data = data + strides[0]
else:
for i from 0<=i<dimensions[0]:
print_elements(data, strides+1, dimensions+1, nd-1, elsize, dtype)
data = data + strides[0]
def test():
"""this function is pure Python"""
arr1 = numpy.array(-1e-30,dtype=numpy.Float64)
arr2 = numpy.array([1.0,2.0,3.0],dtype=numpy.Float64)
arr3 = numpy.arange(9,dtype=numpy.Float64)
arr3.shape = 3,3
four = 4
arr4 = numpy.array(['one','two',3,four],dtype=numpy.object_)
arr5 = numpy.array([1,2,3]) # int types not (yet) supported by print_elements
for arr in [arr1,arr2,arr3,arr4,arr5]:
print_array_info(arr)
现在,如果您编译并安装上面的 test.pyx,的输出应该如下所示:
-=-=-=-=-=-=-=-=-=-=
printing array info for ndarray at 0x8184508
print number of dimensions: 0
address of strides: 0xb764f7ec
strides:
memory dump:
-1e-30
-=-=-=-=-=-=-=-=-=-=
-=-=-=-=-=-=-=-=-=-=
printing array info for ndarray at 0x8190060
print number of dimensions: 1
address of strides: 0x818453c
strides:
stride 0: 8
memory dump:
1.0
2.0
3.0
-=-=-=-=-=-=-=-=-=-=
-=-=-=-=-=-=-=-=-=-=
printing array info for ndarray at 0x82698a0
print number of dimensions: 2
address of strides: 0x8190098
strides:
stride 0: 24
stride 1: 8
memory dump:
0.0
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
-=-=-=-=-=-=-=-=-=-=
-=-=-=-=-=-=-=-=-=-=
printing array info for ndarray at 0x821d6e0
print number of dimensions: 1
address of strides: 0x818ed74
strides:
stride 0: 4
memory dump:
one
two
3
4
-=-=-=-=-=-=-=-=-=-=
-=-=-=-=-=-=-=-=-=-=
printing array info for ndarray at 0x821d728
print number of dimensions: 1
address of strides: 0x821d75c
strides:
stride 0: 4
memory dump:
print_elements() not (yet) implemented for dtype int32
-=-=-=-=-=-=-=-=-=-=
http://pytables.sourceforge.net/ py tables 项目广泛使用了 Pyrex 和 numarray。有关更多想法,请参见 pytables 源代码。
=另请参见= [“秘籍/数组结构 _ 和 _ 派热克斯”]
SWIG Numpy 示例
SWIG Numpy 示例
介绍
这些都很简单!使用 numpy.i 接口文件的 NumPy 和 SWIG 示例。还有一个 MinGW 部分是为可能想在 Win32 环境中使用它们的人准备的。下面的代码是 C,而不是 C++。
这里包含的信息首先是由比尔·斯波茨在他的文章中提供的。NumPy ,还有那个!可以使用以下命令签出的数字 SVN:
svn co http://scipy.org/svn/numpy/trunk numpy
* The !NumPy+SWIG manual is available here: ```py1
【http://scipy . org/SVN/numpy/trunk/doc/swig/doc/numpy _ swig . pdf】(http://scipy . org/SVN/numpy/trunk/doc/swig/doc/numpy _ swig . pdf)__
numpy . I 文件可从 SVN:```2`【http://scipy . org/SVN/numpy/trunk/doc/swi 下载
初始设置
海湾合作委员会和瑞士政府
检查 gcc 和 SWIG 是否可用(已知路径):
swig -version
和
gcc -v
两者都应该输出一些文本...
修改 pyfragments.swg 文件(仅限 MinGW)
这是来自我自己的测试,运行 SWIG 1 . 3 . 36 版和 gcc 3 . 4 . 5 版(mingw-vista special r3)。我必须从源代码中删除“静态”语句,否则你的 SWIGed 源代码将无法编译。文件中只有两个“静态”语句,都需要删除。这是我的修改版本: pyfragments.swg
编译和测试
必须首先写入每个模块特有的 setup.py 文件。我的是基于参考设置。py 在http://scipy.org/svn/numpy/trunk/doc/swig/test/中提供,增加了自动处理痛点。
在类似 un*x 的系统上,命令行是:
python setup.py build
在 Win32 环境(cygwin 或 cmd)中,安装命令行为(用于 MinGW):
python setup.py build --compiler=mingw32
该命令处理 SWIG 过程(包装 C 和 Python 代码的生成)和 gcc 编译。生成的模块(pyd 文件)构建在build\lib.XXX
目录中(例如,对于 Python 2.5 安装和 Win32 机器上的build\lib.win32-2.5
目录)。
一个简单的 ARGOUT_ARRAY1 例子
这是范围函数的重新实现。这个模块叫做 ezrange。ARGOUT_ARRAY1
要记住的一点是,数组的维度必须从 Python 中传递。
来自比尔·斯波茨的文章:python 用户没有传入这些数组,它们只是被返回。对于指定维度的情况,python 用户必须提供该维度作为参数。
这对于像numpy.arange(N)
这样的函数很有用,对于这些函数,返回数组的大小是预先知道的,并传递给 C 函数。
对于跟随array_out = function(array_in)
的函数,其中数组的大小 out _ not是预先已知的,并且取决于 C 中分配的内存,请参见[:Cookbook/SWIG 内存解除分配]中给出的示例。
C 源(ezrange.c 和 ezrange.h)
以下是 ezrange.h 文件:
void range(int *rangevec, int n);
以下是 ezrange.c 文件:
void range(int *rangevec, int n)
{
int i;
for (i=0; i< n; i++)
rangevec[i] = i;
}
接口文件(ezrange.i)
这是 ezrange.i 文件。
%module ezrange
%{
#define SWIG_FILE_WITH_INIT
#include "ezrange.h"
%}
%include "numpy.i"
%init %{
import_array();
%}
%apply (int* ARGOUT_ARRAY1, int DIM1) {(int* rangevec, int n)}
%include "ezrange.h"
别忘了你还需要同一个目录下的 numpy.i 文件。
安装文件(setup.py)
这是我的设置文件:
#! /usr/bin/env python
# System imports
from distutils.core import *
from distutils import sysconfig
# Third-party modules - we depend on numpy for everything
import numpy
# Obtain the numpy include directory. This logic works across numpy versions.
try:
numpy_include = numpy.get_include()
except AttributeError:
numpy_include = numpy.get_numpy_include()
# ezrange extension module
_ezrange = Extension("_ezrange",
["ezrange.i","ezrange.c"],
include_dirs = [numpy_include],
)
# ezrange setup
setup( name = "range function",
description = "range takes an integer and returns an n element int array where each element is equal to its index",
author = "Egor Zindy",
version = "1.0",
ext_modules = [_ezrange]
)
编译模块
安装命令行是:
python setup.py build
或者
python setup.py build --compiler=mingw32
取决于你的环境。
测试模块
如果一切按计划进行,在build\lib.XXX
目录中应该有一个_ezrange.pyd
文件可用。您需要将文件复制到ezrange.py
文件所在的目录中(由 swig 生成),在这种情况下,以下内容将起作用(在 python 中):
>>> import ezrange
>>> ezrange.range(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
一个简单的 INPLACE_ARRAY1 例子
本示例将传递给它的一维数组的元素加倍。操作就地完成,这意味着传递给函数的数组发生了变化。
C 源(inplace.c 和 inplace.h)
以下是原位文件:
void inplace(double *invec, int n);
以下是 inplace.c 文件:
void inplace(double *invec, int n)
{
int i;
for (i=0; i<n; i++)
{
invec[i] = 2*invec[i];
}
}
接口文件(inplace.i)
这里是 inplace.i 接口文件:
%module inplace
%{
#define SWIG_FILE_WITH_INIT
#include "inplace.h"
%}
%include "numpy.i"
%init %{
import_array();
%}
%apply (double* INPLACE_ARRAY1, int DIM1) {(double* invec, int n)}
%include "inplace.h"
安装文件(setup.py)
这是我的设置文件:
#! /usr/bin/env python
# System imports
from distutils.core import *
from distutils import sysconfig
# Third-party modules - we depend on numpy for everything
import numpy
# Obtain the numpy include directory. This logic works across numpy versions.
try:
numpy_include = numpy.get_include()
except AttributeError:
numpy_include = numpy.get_numpy_include()
# inplace extension module
_inplace = Extension("_inplace",
["inplace.i","inplace.c"],
include_dirs = [numpy_include],
)
# NumyTypemapTests setup
setup( name = "inplace function",
description = "inplace takes a double array and doubles each of its elements in-place.",
author = "Egor Zindy",
version = "1.0",
ext_modules = [_inplace]
)
编译模块
安装命令行是:
python setup.py build
或者
python setup.py build --compiler=mingw32
取决于你的环境。
测试模块
如果一切按计划进行,在build\lib.XXX
目录中应该有一个_inplace.pyd
文件可用。您需要将文件复制到inplace.py
文件所在的目录中(由 swig 生成),在这种情况下,以下内容将起作用(在 python 中):
>>> import numpy
>>> import inplace
>>> a = numpy.array([1,2,3],'d')
>>> inplace.inplace(a)
>>> a
array([2., 4., 6.])
一个简单的 ARGOUTVIEW_ARRAY1 示例
大胖子多重警告
请注意,比尔·斯波茨建议不要使用 argout_view 数组,除非绝对必要:
Argoutview arrays are for when your C code provides you with a view of its internal data and does not require any memory to be allocated by the user. This can be dangerous. There is almost no way to guarantee that the internal data from the C code will remain in existence for the entire lifetime of the !NumPy array that encapsulates it. If the user destroys the object that provides the view of the data before destroying the !NumPy array, then using that array my result in bad memory references or segmentation faults. Nevertheless, there are situations, working with large data sets, where you simply have no other choice.
Python 不处理内存分配问题,正如特拉维斯·奥列芬特在这里所说: 1
The tricky part, however, is memory management. How does the memory get deallocated? The suggestions have always been something similar to “make sure the memory doesn’t get deallocated before the !NumPy array disappears.” This is nice advice, but not generally helpful as it basically just tells you to create a memory leak.
内存释放也很难自动处理,因为没有简单的方法来完成模块“终结”。有一个Py_InitModule()
函数,但是没有任何东西可以处理删除/销毁/终结(这将在 Python 3000 中处理,如 PEP3121 中所述。在我的例子中,我使用 python 模块退出,但是一定有更好的方法。
说了这么多,如果你没有其他选择,这里有一个使用 ARGOUTVIEW _ ARRAY1 的例子。和往常一样,欢迎评论!
该模块声明一个内存块和几个函数:ezview . set _ one()将内存块中的所有元素(双精度)设置为 1,并返回一个 numpy 数组,该数组是内存块的视图。 ezview.get_view()只是返回内存块的视图。* ezview.finalize()负责内存释放(这是本例的薄弱部分)。
C 来源(ezview.c 和 ezview.h)
以下是 ezview.h 文件:
void set_ones(double *array, int n);
以下是 ezview.c 文件:
#include <stdio.h>
#include <stdlib.h>
#include "ezview.h"
void set_ones(double *array, int n)
{
int i;
if (array == NULL)
return;
for (i=0;i<n;i++)
array[i] = 1.;
}
接口文件(ezview.i)
以下是 ezview.i 界面文件:
%module ezview
%{
#define SWIG_FILE_WITH_INIT
#include "ezview.h"
double *my_array = NULL;
int my_n = 10;
void __call_at_begining()
{
printf("__call_at_begining...\n");
my_array = (double *)malloc(my_n*sizeof(double));
}
void __call_at_end(void)
{
printf("__call_at_end...\n");
if (my_array != NULL)
free(my_array);
}
%}
%include "numpy.i"
%init %{
import_array();
__call_at_begining();
%}
%apply (double** ARGOUTVIEW_ARRAY1, int *DIM1) {(double** vec, int* n)}
%include "ezview.h"
%rename (set_ones) my_set_ones;
%inline %{
void finalize(void){
__call_at_end();
}
void get_view(double **vec, int* n) {
*vec = my_array;
*n = my_n;
}
void my_set_ones(double **vec, int* n) {
set_ones(my_array,my_n);
*vec = my_array;
*n = my_n;
}
%}
不要忘记,您还需要同一个目录中的 numpy.i 文件。
安装文件(setup.py)
这是我的设置文件:
#! /usr/bin/env python
# System imports
from distutils.core import *
from distutils import sysconfig
# Third-party modules - we depend on numpy for everything
import numpy
# Obtain the numpy include directory. This logic works across numpy versions.
try:
numpy_include = numpy.get_include()
except AttributeError:
numpy_include = numpy.get_numpy_include()
# view extension module
_ezview = Extension("_ezview",
["ezview.i","ezview.c"],
include_dirs = [numpy_include],
)
# NumyTypemapTests setup
setup( name = "ezview module",
description = "ezview provides 3 functions: set_ones(), get_view() and finalize(). set_ones() and get_view() provide a view on a memory block allocated in C, finalize() takes care of the memory deallocation.",
author = "Egor Zindy",
version = "1.0",
ext_modules = [_ezview]
)
编译模块
安装命令行是:
python setup.py build
或者
python setup.py build --compiler=mingw32
取决于你的环境。
测试模块
如果一切按计划进行,在build\lib.XXX
目录中应该有一个_ezview.pyd
文件可用。您需要将文件复制到ezview.py
文件所在的目录中(由 swig 生成),在这种情况下,以下内容将起作用(在 python 中):
测试代码 test_ezview.py 如下:
import atexit
import numpy
print "first message is from __call_at_begining()"
import ezview
#There is no easy way to finalize the module (see PEP3121)
atexit.register(ezview.finalize)
a = ezview.set_ones()
print "\ncalling ezview.set_ones() - now the memory block is all ones.\nReturned array (a view on the allocated memory block) is:"
print a
print "\nwe're setting the array using a[:]=arange(a.shape[0])\nThis changes the content of the allocated memory block:"
a[:] = numpy.arange(a.shape[0])
print a
print "\nwe're now deleting the array - this only deletes the view,\nnot the allocated memory!"
del a
print "\nlet's get a new view on the allocated memory, should STILL contain [0,1,2,3...]"
b = ezview.get_view()
print b
print "\nnext message from __call_at_end() - finalize() registered via module atexit"
启动 test_ezview.py,希望会发生以下情况:
~> python test_ezview.py
first message is from __call_at_begining()
__call_at_begining...
calling ezview.set_ones() - now the memory block is all ones.
Returned array (a view on the allocated memory block) is:
[ 1\. 1\. 1\. 1\. 1\. 1\. 1\. 1\. 1\. 1.]
we re setting the array using a[:]=arange(a.shape[0])
This changes the content of the allocated memory block:
[ 0\. 1\. 2\. 3\. 4\. 5\. 6\. 7\. 8\. 9.]
we re now deleting the array - this only deletes the view,
not the allocated memory!
let s get a new view on the allocated memory, should STILL contain [0,1,2,3...]
[ 0\. 1\. 2\. 3\. 4\. 5\. 6\. 7\. 8\. 9.]
next message from __call_at_end() - finalize() registered via module atexit
__call_at_end...
使用 errno 和 python 异常处理错误
我已经测试了几个月了,这是我想到的最好的方法。如果有人知道更好的方法,请告诉我。
在 opengroup 网站上,lvalue errno 被许多函数用来返回错误值。其思想是在调用函数之前将全局变量 errno 设置为 0(用 swig 的说法:$action),然后进行检查。如果 errno 为非零,则根据 errno 的值,会生成一个 python 异常,其中包含有意义的消息。
以下示例包含两个示例:第一个示例在检查数组索引是否有效时使用 errno。第二个示例使用 errno 向用户通知 malloc()问题。
碳源
以下是ezer . h文件:
int val(int *array, int n, int index);
void alloc(int n);
以下是 ezerr.c 文件:
#include <stdlib.h>
#include <errno.h>
#include "ezerr.h"
//return the array element defined by index
int val(int *array, int n, int index)
{
int value=0;
if (index < 0 || index >=n)
{
errno = EPERM;
goto end;
}
value = array[index];
end:
return value;
}
//allocate (and free) a char array of size n
void alloc(int n)
{
char *array;
array = (char *)malloc(n*sizeof(char));
if (array == NULL)
{
errno = ENOMEM;
goto end;
}
//don't keep the memory allocated...
free(array);
end:
return;
}
接口文件(ezerr.i)
这里是ezer . I界面文件:
%module ezerr
%{
#include <errno.h>
#include "ezerr.h"
#define SWIG_FILE_WITH_INIT
%}
%include "numpy.i"
%init %{
import_array();
%}
%exception
{
errno = 0;
$action
if (errno != 0)
{
switch(errno)
{
case EPERM:
PyErr_Format(PyExc_IndexError, "Index out of range");
break;
case ENOMEM:
PyErr_Format(PyExc_MemoryError, "Failed malloc()");
break;
default:
PyErr_Format(PyExc_Exception, "Unknown exception");
}
SWIG_fail;
}
}
%apply (int* IN_ARRAY1, int DIM1) {(int *array, int n)}
%include "ezerr.h"
注意 SWIG_fail ,这是转到 fail 的宏,以防有其他清理代码要执行(谢谢比尔!).
不要忘记,您还需要同一个目录中的 numpy.i 文件。
安装文件(setup.py)
这是我的设置文件:
#! /usr/bin/env python
# System imports
from distutils.core import *
from distutils import sysconfig
# Third-party modules - we depend on numpy for everything
import numpy
# Obtain the numpy include directory. This logic works across numpy versions.
try:
numpy_include = numpy.get_include()
except AttributeError:
numpy_include = numpy.get_numpy_include()
# err extension module
ezerr = Extension("_ezerr",
["ezerr.i","ezerr.c"],
include_dirs = [numpy_include],
extra_compile_args = ["--verbose"]
)
# NumyTypemapTests setup
setup( name = "err test",
description = "A simple test to demonstrate the use of errno and python exceptions",
author = "Egor Zindy",
version = "1.0",
ext_modules = [ezerr]
)
编译模块
安装命令行是:
python setup.py build
或者
python setup.py build --compiler=mingw32
取决于你的环境。
测试模块
如果一切按计划进行,在build\lib.XXX
目录中应该有一个_ezerr.pyd
文件可用。您需要将文件复制到ezerr.py
文件所在的目录中(由 swig 生成),在这种情况下,以下内容将起作用(在 python 中):
测试代码 test_err.py 如下:
import traceback,sys
import numpy
import ezerr
print "\n--- testing ezerr.val() ---"
a = numpy.arange(10)
indexes = [5,20,-1]
for i in indexes:
try:
value = ezerr.val(a,i)
except:
print ">> failed for index=%d" % i
traceback.print_exc(file=sys.stdout)
else:
print "using ezerr.val() a[%d]=%d - should be %d" % (i,value,a[i])
print "\n--- testing ezerr.alloc() ---"
amounts = [1,-1] #1 byte, -1 byte
for n in amounts:
try:
ezerr.alloc(n)
except:
print ">> could not allocate %d bytes" % n
traceback.print_exc(file=sys.stdout)
else:
print "allocated (and deallocated) %d bytes" % n
启动 test_err.py,希望会发生以下情况:
~> python test_err.py
--- testing ezerr.val() ---
using ezerr.val() a[5]=5 - should be 5
>> failed for index=20
Traceback (most recent call last):
File "test_err.py", line 11, in <module>
value = ezerr.val(a,i)
IndexError: Index out of range
>> failed for index=-1
Traceback (most recent call last):
File "test_err.py", line 11, in <module>
value = ezerr.val(a,i)
IndexError: Index out of range
--- testing ezerr.alloc() ---
allocated (and deallocated) 1 bytes
>> could not allocate -1 bytes
Traceback (most recent call last):
File "test_err.py", line 23, in <module>
ezerr.alloc(n)
MemoryError: Failed malloc()
Dot 产品示例(来自 Bill Spotz 的文章)
比尔·斯波茨文章中给出的最后一个例子是点积函数。这是一个充实的版本。
C 源(点 C 和点 h)
这里是 dot.h 文件:
double dot(int len, double* vec1, double* vec2);
以下是点 c 文件:
#include <stdio.h>
#include "dot.h"
double dot(int len, double* vec1, double* vec2)
{
int i;
double d;
d = 0;
for(i=0;i<len;i++)
d += vec1[i]*vec2[i];
return d;
}
接口文件(dot.i 和 numpy.i)
以下是完整的文件:
%module dot
%{
#define SWIG_FILE_WITH_INIT
#include "dot.h"
%}
%include "numpy.i"
%init %{
import_array();
%}
%apply (int DIM1, double* IN_ARRAY1) {(int len1, double* vec1), (int len2, double* vec2)}
%include "dot.h"
%rename (dot) my_dot;
%inline %{
double my_dot(int len1, double* vec1, int len2, double* vec2) {
if (len1 != len2) {
PyErr_Format(PyExc_ValueError, "Arrays of lengths (%d,%d) given", len1, len2);
return 0.0;
}
return dot(len1, vec1, vec2);
}
%}
安装文件(setup.py)
这是设置文件:
#! /usr/bin/env python
# System imports
from distutils.core import *
from distutils import sysconfig
# Third-party modules - we depend on numpy for everything
import numpy
# Obtain the numpy include directory. This logic works across numpy versions.
try:
numpy_include = numpy.get_include()
except AttributeError:
numpy_include = numpy.get_numpy_include()
# dot extension module
_dot = Extension("_dot",
["dot.i","dot.c"],
include_dirs = [numpy_include],
)
# dot setup
setup( name = "Dot product",
description = "Function that performs a dot product (numpy.i: a SWIG Interface File for NumPy)",
author = "Egor Zindy (based on the setup.py file available in the numpy tree)",
version = "1.0",
ext_modules = [_dot]
)
编译模块
安装命令行是:
python setup.py build
或者
python setup.py build --compiler=mingw32
取决于你的环境。
测试
如果一切按计划进行,在build\lib.XXX
目录中应该有一个_dot.pyd
文件可用。您需要将文件复制到dot.py
文件所在的目录中(由 swig 生成),在这种情况下,以下内容将起作用(在 python 中):
>>> import dot
>>> dot.dot([1,2,3],[1,2,3])
14.0
结论
这就是所有人(现在)!和往常一样,欢迎评论!
- '''待办事项'':代码清理并将示例移到!SciPy/!NumPy 存储库?
看,egor
SWIG 和 Numpy
SWIG 和 Numpy
请注意,对于 NumPy (SVN)的当前版本,该目录包含一个完整的工作示例,带有简单的 SWIG 类型映射,还包括一个适当的文件,因此您可以使用标准的 Python 机制安装它。这应该有助于你快速起床和跑步。
为了感受如何编写一个真正的极简界面,下面是简单的 SWIG 界面文件 umfpack.i
(这是针对 SWIG<1 . 3 . 29 版)的相关部分,用来包装 UMFPACK 稀疏线性求解器库。完整的界面可以在 SciPy SVN 存储库中的目录中找到。如果您正在使用 SWIG>1 . 3 . 29 版本,请参考 SciPy SVN 存储库中的文件,该文件略有不同。
/*!
Gets PyArrayObject from a PyObject.
*/
PyArrayObject *helper_getCArrayObject( PyObject *input, int type,
int minDim, int maxDim ) {
PyArrayObject *obj;
if (PyArray_Check( input )) {
obj = (PyArrayObject *) input;
if (!PyArray_ISCARRAY( obj )) {
PyErr_SetString( PyExc_TypeError, "not a C array" );
return NULL;
}
obj = (PyArrayObject *)
PyArray_ContiguousFromAny( input, type, minDim, maxDim );
if (!obj) return NULL;
} else {
PyErr_SetString( PyExc_TypeError, "not an array" );
return NULL;
}
return obj;
}
%}
/*!
Use for arrays as input arguments. Could be also used for changing an array
in place.
@a rtype ... return this C data type
@a ctype ... C data type of the C function
@a atype ... PyArray_* suffix
*/
#define ARRAY_IN( rtype, ctype, atype ) \
%typemap( python, in ) (ctype *array) { \
PyArrayObject *obj; \
obj = helper_getCArrayObject( $input, PyArray_##atype, 1, 1 ); \
if (!obj) return NULL; \
$1 = (rtype *) obj->data; \
Py_DECREF( obj ); \
};
ARRAY_IN( int, const int, INT )
%apply const int *array {
const int Ap [ ],
const int Ai [ ]
};
ARRAY_IN( long, const long, LONG )
%apply const long *array {
const long Ap [ ],
const long Ai [ ]
};
ARRAY_IN( double, const double, DOUBLE )
%apply const double *array {
const double Ax [ ],
const double Az [ ],
const double B [ ]
};
ARRAY_IN( double, double, DOUBLE )
%apply double *array {
double X [ ]
};
被包装的函数可能是这样的:
int umfpack_di_solve( int sys, const int Ap [ ], const int Ai [ ],
const double Ax [ ], double X [ ], const double B [ ],
... );
附件
SWIG 内存释放
SWIG 内存释放
介绍
秘籍描述
本秘籍描述了当相应的 Python numpy 数组对象被销毁时,通过 C 语言中的malloc()
调用分配的内存块的自动解除分配。秘籍使用 SWIG 和修改后的numpy.i
辅助文件。
更具体地说,新的片段被添加到现有的numpy.i
中,以处理数组的自动解除分配,数组的大小事先不知道。与原始片段一样,通过调用PyArray_SimpleNewFromData()
,一块malloc()
内存可以被转换为返回的 numpy python 对象。但是,返回的 python 对象是使用PyCObject_FromVoidPtr()
创建的,这确保了当 Python 对象被销毁时,分配的内存会被自动处置。下面的例子展示了如何使用这些新片段来避免内存泄漏。
由于新碎片基于_ARGOUTVIEW_
碎片,因此选择了名称_ARGOUTVIEWM_
,其中M
代表托管。所有托管片段(ARRAY1、2 和 3、FARRAY1、2 和 3)都已实现,现在已经过广泛测试。
哪里可以拿到文件
目前,修改后的 numpy.i 文件可在此获得(上次更新时间为 2012-04-22):【http://ezwidgets.googlecode.com/svn/trunk/numpy/numpy.i】http://ezwidgets . googlecode . com/SVN/trunk/numpy/py fragments . swg
代码是如何产生的
最初的内存释放代码是特拉维斯·奥列芬特写的(见http://blog.enthought.com/?p=62),据我所知,这些聪明的人是第一个在 swig 文件中使用它的人(见http://niftilib.sourceforge.net/pynifti,文件 nifticlib.i)。Lisandro Dalcin 随后指出了一个使用协同对象的简化实现,Travis 在这个更新的博文中详细介绍了这个实现。
如何使用新片段
重要步骤
在你的文件中,函数%init 使用你已经知道的相同的import_array()
调用:
%init %{
import_array();
%}
...然后只需使用 ARGOUTVIEWM_ARRAY1 而不是 ARGOUTVIEW_ARRAY1,当 python 数组被销毁时,内存释放会自动处理(参见下面的示例)。
一个简单的 ARGOUTVIEWM_ARRAY1 例子
C 语言中的 SWIG 包装函数创建一个 N 整数数组,使用malloc()
分配内存。从 python 中,这个函数被重复调用,创建的数组被销毁(M 次)。
使用 numpy.i 中提供的 ARGOUTVIEW_ARRAY1,这会造成内存泄漏(我知道 ARGOUTVIEW_ARRAY1 不是为此目的而设计的,但它很有诱惑力!).
改为使用 ARGOUTVIEWM_ARRAY1 片段,当数组被删除时,用malloc()
分配的内存将被自动解除分配。
Python 测试程序使用 ARGOUTVIEW_ARRAY1 和 ARGOUTVIEWM_ARRAY1 创建和删除 1024^2 ints 数组 2048 次,当内存分配失败时,会在 c 语言中生成一个异常,并在 python 中捕获该异常,显示哪一次迭代最终导致分配失败。
C 来源(ezalloc.c 和 ezalloc.h)
以下是 ezalloc.h 文件:
void alloc(int ni, int** veco, int *n);
以下是 ezalloc.c 文件:
#include <stdio.h>
#include <errno.h>
#include "ezalloc.h"
void alloc(int ni, int** veco, int *n)
{
int *temp;
temp = (int *)malloc(ni*sizeof(int));
if (temp == NULL)
errno = ENOMEM;
//veco is either NULL or pointing to the allocated block of memory...
*veco = temp;
*n = ni;
}
接口文件(ezalloc.i)
这个文件(这里有: ezalloc.i )做了一些有趣的事情:就像我在介绍中说的,调用%init
部分的import_array()
函数现在也初始化了内存释放代码。这里没有其他要补充的了。如果内存分配失败,会产生异常。在代码构造的几次迭代之后,使用errno
和SWIG_fail
是我想到的最简单的方法。*在本例中,创建了两个内联函数,一个使用 ARGOUTVIEW_ARRAY1,另一个使用 ARGOUTVIEWM_ARRAY1。这两个函数都使用alloc()
函数(参见 ezalloc.h 和 ezalloc.c)。
%module ezalloc
%{
#include <errno.h>
#include "ezalloc.h"
#define SWIG_FILE_WITH_INIT
%}
%include "numpy.i"
%init %{
import_array();
%}
%apply (int** ARGOUTVIEWM_ARRAY1, int *DIM1) {(int** veco1, int* n1)}
%apply (int** ARGOUTVIEW_ARRAY1, int *DIM1) {(int** veco2, int* n2)}
%include "ezalloc.h"
%exception
{
errno = 0;
$action
if (errno != 0)
{
switch(errno)
{
case ENOMEM:
PyErr_Format(PyExc_MemoryError, "Failed malloc()");
break;
default:
PyErr_Format(PyExc_Exception, "Unknown exception");
}
SWIG_fail;
}
}
%rename (alloc_managed) my_alloc1;
%rename (alloc_leaking) my_alloc2;
%inline %{
void my_alloc1(int ni, int** veco1, int *n1)
{
/* The function... */
alloc(ni, veco1, n1);
}
void my_alloc2(int ni, int** veco2, int *n2)
{
/* The function... */
alloc(ni, veco2, n2);
}
%}
别忘了你需要在同一个目录下的 numpy.i 文件来编译这个。
安装文件(setup_alloc.py)
这是设置文件:
#! /usr/bin/env python
# System imports
from distutils.core import *
from distutils import sysconfig
# Third-party modules - we depend on numpy for everything
import numpy
# Obtain the numpy include directory. This logic works across numpy versions.
try:
numpy_include = numpy.get_include()
except AttributeError:
numpy_include = numpy.get_numpy_include()
# alloc extension module
_ezalloc = Extension("_ezalloc",
["ezalloc.i","ezalloc.c"],
include_dirs = [numpy_include],
extra_compile_args = ["--verbose"]
)
# NumyTypemapTests setup
setup( name = "alloc functions",
description = "Testing managed arrays",
author = "Egor Zindy",
version = "1.0",
ext_modules = [_ezalloc]
)
编译模块
安装命令行是(在 Windows 中,使用 mingw):
$> python setup_alloc.py build --compiler=mingw32
或者在 UN*X 中,简单地说
$> python setup_alloc.py build
测试模块
如果一切按计划进行,在build\lib.XXX
目录中应该有一个_ezalloc.pyd
文件可用。文件需要复制到ezalloc.py
文件的目录中(swig 生成)。
SVN 存储库中提供了一个 python 测试程序( test_alloc.py ),转载如下:
import ezalloc
n = 2048
# this multiplied by sizeof(int) to get size in bytes...
#assuming sizeof(int)=4 on a 32bit machine (sorry, it's late!)
m = 1024 * 1024
err = 0
print "ARGOUTVIEWM_ARRAY1 (managed arrays) - %d allocations (%d bytes each)" % (n,4*m)
for i in range(n):
try:
#allocating some memory
a = ezalloc.alloc_managed(m)
#deleting the array
del a
except:
err = 1
print "Step %d failed" % i
break
if err == 0:
print "Done!\n"
print "ARGOUTVIEW_ARRAY1 (unmanaged, leaking) - %d allocations (%d bytes each)" % (n,4*m)
for i in range(n):
try:
#allocating some memory
a = ezalloc.alloc_leaking(m)
#deleting the array
del a
except:
err = 1
print "Step %d failed" % i
break
if err == 0:
print "Done? Increase n!\n"
然后,一个
$> python test_alloc.py
将产生类似如下的输出:
ARGOUTVIEWM_ARRAY1 (managed arrays) - 2048 allocations (4194304 bytes each)
Done!
ARGOUTVIEW_ARRAY1 (unmanaged, leaking) - 2048 allocations (4194304 bytes each)
Step 483 failed
每次删除数组视图时,非托管数组都会泄漏内存。被管理的将无缝删除内存块。这在 Windows XP 和 Linux 中都进行了测试。
一个简单的 ARGOUTVIEWM_ARRAY2 例子
下面的例子展示了如何从 C 返回一个二维数组,这也得益于自动内存释放。
使用 SWIG/numpy.i 包装一个简单的“裁剪”函数,并返回输入数组的一部分。当用作array_out = crop.crop(array_in, d1_0,d1_1, d2_0,d2_1)
时,相当于原生 numpy 切片array_out = array_in[d1_0:d1_1, d2_0:d2_1]
。
碳源(作物碳和作物氢)
这是作物文件:
void crop(int *arr_in, int dim1, int dim2, int d1_0, int d1_1, int d2_0, int d2_1, int **arr_out, int *dim1_out, int *dim2_out);
这是 crop.c 文件:
#include <stdlib.h>
#include <errno.h>
#include "crop.h"
void crop(int *arr_in, int dim1, int dim2, int d1_0, int d1_1, int d2_0, int d2_1, int **arr_out, int *dim1_out, int *dim2_out)
{
int *arr=NULL;
int dim1_o=0;
int dim2_o=0;
int i,j;
//value checks
if ((d1_1 < d1_0) || (d2_1 < d2_0) ||
(d1_0 >= dim1) || (d1_1 >= dim1) || (d1_0 < 0) || (d1_1 < 0) ||
(d2_0 >= dim2) || (d2_1 >= dim2) || (d2_0 < 0) || (d2_1 < 0))
{
errno = EPERM;
goto end;
}
//output sizes
dim1_o = d1_1-d1_0;
dim2_o = d2_1-d2_0;
//memory allocation
arr = (int *)malloc(dim1_o*dim2_o*sizeof(int));
if (arr == NULL)
{
errno = ENOMEM;
goto end;
}
//copying the cropped arr_in region to arr (naive implementation)
printf("\n--- d1_0=%d d1_1=%d (rows) -- d2_0=%d d2_1=%d (columns)\n",d1_0,d1_1,d2_0,d2_1);
for (j=0; j<dim1_o; j++)
{
for (i=0; i<dim2_o; i++)
{
arr[j*dim2_o+i] = arr_in[(j+d1_0)*dim2+(i+d2_0)];
printf("%d ",arr[j*dim2_o+i]);
}
printf("\n");
}
printf("---\n\n");
end:
*dim1_out = dim1_o;
*dim2_out = dim2_o;
*arr_out = arr;
}
接口文件(作物. I)
文件(在这里可以获得: crop.i )做了一些有趣的事情:数组维度 DIM1 和 DIM2 的顺序与 Python 端的 array.shape 相同。在图像的行主数组定义中,DIM1 代表行数,DIM2 代表列数。使用 errno 库,当内存分配失败(ENOMEM)或索引出现问题(EPERM)时会产生异常。
%module crop
%{
#include <errno.h>
#include "crop.h"
#define SWIG_FILE_WITH_INIT
%}
%include "numpy.i"
%init %{
import_array();
%}
%exception crop
{
errno = 0;
$action
if (errno != 0)
{
switch(errno)
{
case EPERM:
PyErr_Format(PyExc_IndexError, "Index error");
break;
case ENOMEM:
PyErr_Format(PyExc_MemoryError, "Not enough memory");
break;
default:
PyErr_Format(PyExc_Exception, "Unknown exception");
}
SWIG_fail;
}
}
%apply (int* IN_ARRAY2, int DIM1, int DIM2) {(int *arr_in, int dim1, int dim2)}
%apply (int** ARGOUTVIEWM_ARRAY2, int* DIM1, int* DIM2) {(int **arr_out, int *dim1_out, int *dim2_out)}
%include "crop.h"
别忘了你需要在同一个目录下的 numpy.i 文件来编译这个。
设置文件(setup_crop.py)
这是 setup_crop.py 文件:
#! /usr/bin/env python
# System imports
from distutils.core import *
from distutils import sysconfig
# Third-party modules - we depend on numpy for everything
import numpy
# Obtain the numpy include directory. This logic works across numpy versions.
try:
numpy_include = numpy.get_include()
except AttributeError:
numpy_include = numpy.get_numpy_include()
# crop extension module
_crop = Extension("_crop",
["crop.i","crop.c"],
include_dirs = [numpy_include],
extra_compile_args = ["--verbose"]
)
# NumyTypemapTests setup
setup( name = "crop test",
description = "A simple crop test to demonstrate the use of ARGOUTVIEWM_ARRAY2",
author = "Egor Zindy",
version = "1.0",
ext_modules = [_crop]
)
测试模块
如果一切按计划进行,在build\lib.XXX
目录中应该有一个_crop.pyd
文件可用。文件需要复制到crop.py
文件的目录中(swig 生成)。
SVN 存储库中提供了一个 python 测试程序( test_crop.py ),转载如下:
import crop
import numpy
a = numpy.zeros((5,10),numpy.int)
a[numpy.arange(5),:] = numpy.arange(10)
b = numpy.transpose([(10 ** numpy.arange(5))])
a = (a*b)[:,1:] #this array is most likely NOT contiguous
print a
print "dim1=%d dim2=%d" % (a.shape[0],a.shape[1])
d1_0 = 2
d1_1 = 4
d2_0 = 1
d2_1 = 5
c = crop.crop(a, d1_0,d1_1, d2_0,d2_1)
d = a[d1_0:d1_1, d2_0:d2_1]
print "returned array:"
print c
print "native slicing:"
print d
输出如下所示:
$ python test_crop.py
[[ 1 2 3 4 5 6 7 8 9]
[ 10 20 30 40 50 60 70 80 90]
[ 100 200 300 400 500 600 700 800 900]
[ 1000 2000 3000 4000 5000 6000 7000 8000 9000]
[10000 20000 30000 40000 50000 60000 70000 80000 90000]]
dim1=5 dim2=9
--- d1_0=2 d1_1=4 (rows) -- d2_0=1 d2_1=5 (columns)
200 300 400 500
2000 3000 4000 5000
---
returned array:
[[ 200 300 400 500]
[2000 3000 4000 5000]]
native slicing:
[[ 200 300 400 500]
[2000 3000 4000 5000]]
numpy.i 负责在需要时使数组连续,因此剩下的唯一需要处理的就是数组方向。
结论和评论
这就是所有人!文件可在[http://code . Google . com/p/ezwidgets/source/browse/# SVN/trunk/numpyGoogle code SVN 上获得。和往常一样,欢迎评论!
看,egor
f2py 和 numpy
f2py 和 numpy
使用 f2py 包装 C 代码
虽然 f2py 最初是为包装 Python 的 Fortran 代码而开发的,但它也可以很容易地用于包装 C 代码。描述包装函数接口的签名文件必须手动创建,函数及其参数必须具有属性。有关签名文件语法的更多信息,请参见 f2py UsersGuide 。
下面是简单的 C 代码
/* File foo.c */
void foo(int n, double *x, double *y) {
int i;
for (i=0;i<n;i++) {
y[i] = x[i] + i;
}
}
和相应的签名文件
! File m.pyf
python module m
interface
subroutine foo(n,x,y)
intent(c) foo ! foo is a C function
intent(c) ! all foo arguments are
! considered as C based
integer intent(hide), depend(x) :: n=len(x) ! n is the length
! of input array x
double precision intent(in) :: x(n) ! x is input array
! (or arbitrary sequence)
double precision intent(out) :: y(n) ! y is output array,
! see code in foo.c
end subroutine foo
end interface
end python module m
要构建包装器,可以创建一个 setup.py 脚本
# File setup.py
def configuration(parent_package='',top_path=None):
from numpy.distutils.misc_util import Configuration
config = Configuration('',parent_package,top_path)
config.add_extension('m',
sources = ['m.pyf','foo.c'])
return config
if __name__ == "__main__":
from numpy.distutils.core import setup
setup(**configuration(top_path='').todict())
并执行:
python setup.py build_src build_ext --inplace
或者可以在命令行中直接调用 f2py 来构建包装器,如下所示:
f2py m.pyf foo.c -c
在这两种情况下,都将在当前目录下创建一个可导入 python 的扩展模块:
>>> import m
>>> print m.__doc__
This module 'm' is auto-generated with f2py (version:2_2130).
Functions:
y = foo(x)
.
>>> print m.foo.__doc__
foo - Function signature:
y = foo(x)
Required arguments:
x : input rank-1 array('d') with bounds (n)
Return objects:
y : rank-1 array('d') with bounds (n)
>>> print m.foo([1,2,3,4,5])
[ 1\. 3\. 5\. 7\. 9.]
>>>