在科学数据分析和可视化领域,IDL(Interactive Data Language)以其高效的数组运算和丰富的绘图功能著称,而 Python 则凭借庞大的生态系统和灵活性成为现代数据科学的主流工具。而IDL-Python Bridge可以结合他们的优势,实现IDL和Python的无缝集成!
本文将以科学数据可视化为例,手把手演示如何通过 IDL-Python Bridge 实现跨语言协作的自动化绘图流程。
一、安装IDL-Python Bridge
首先安装IDL和Python, 可参考:IDL-Python Bridge 安装及兼容版本列表(https://www.cnblogs.com/enviidl/p/18318690)
二、安装Python库
在IDL中调用python的功能,注意要正确安装Python中所使用到的库,例如如下绘图代码需要用到matplotlib库。
IDL> ran=python.import('numpy.random')
% Loaded DLM: PYTHON27.
IDL> arr=ran.rand(100)
IDL> plt=python.Import('matplotlib.pyplot')
IDL> p=plt.plot(arr)
IDL> void=plt.show(block=0)
需要在python中安装对应的库,与第一步安装教程中介绍的一致,依然推荐在miniconda安装,可在miniconda中执行如下命令:
conda activate py310
conda install matplotlib
安装好之后,再次执行上面的代码,查看结果。如果报如下类似错误:

说明缺少PyQt相关依赖,在miniconda安装目录查看实际需要安装的PyQt版本,例如PyQt5或PyQt6。
C:\ProgramData\miniconda3\envs\py310\Lib\site-packages
之后可通过类似如下命令安装依赖,注意切换国内源,默认源因为网络问题往往安装不完全。同样在miniconda命令行执行类似如下命令:
conda activate py310
pip install sip
pip install PyQt6 -i https:
pip install PyQt6-tools -i https:
安装好所需的库之后,在IDL命令行再次执行绘图代码:
IDL> ran=python.import('numpy.random')
% Loaded DLM: PYTHON27.
IDL> arr=ran.rand(100)
IDL> plt=python.Import('matplotlib.pyplot')
IDL> p=plt.plot(arr)
IDL> void=plt.show(block=0)
正常情况下应该能看到如下结果:

三、IDL Python无缝调用
安装好IDL-Python Bridge及Python所需的库之后,接下来才进入正题:IDL与Python的无缝调用。
1. 在IDL中调用Python
刚才介绍的在IDL调用Python的方式是通过导入Python类的形式实现:
IDL> ran=python.import('numpy.random')
% Loaded DLM: PYTHON27.
IDL> arr=ran.rand(100)
IDL> plt=python.Import('matplotlib.pyplot')
IDL> p=plt.plot(arr)
IDL> void=plt.show(block=0)
此外,还有另外一种方法:在IDL控制台中输入 >>>,然后回车,即可进入Python命令行模式。当不输入命令,直接回车时,退出Python命令行模式。如下所示:
IDL> >>>
>>> import matplotlib.pyplot as plt
>>> import numpy.random as ran
>>> arr = ran.rand(100)
>>> p = plt.plot(arr)
>>> plt.show()
>>>
运行效果与导入Pyhton类的方式一样。两种方式各有优劣,>>>的方式可以完全保留Python原始的编程风格。
接下来,让我们做几个简单的示例:
(1)绘制多条折线图
在IDL命令行中输入>>>,进入Python命令行模式。输入如下命令:
import matplotlib.pyplot as plt
plt.plot([5, 15], label='Rice')
plt.plot([3, 6], label='Oil')
plt.plot([8.0010, 14.2], label='Wheat')
plt.plot([1.95412, 6.98547, 5.41411, 5.99, 7.9999], label='Coffee')
plt.title("Interactive Plot")
plt.xlabel("X-axis")
plt.ylabel("Y-axis")
plt.legend()
plt.show()
如果你是直接复制上面的代码然后粘贴到命令行里,需要注意两点:
第一,在IDLDE里只能逐行粘贴执行,如果多行粘贴不会逐行执行(这简直是懒人地狱😖,此时你可以在vscode中安装IDL for VSCode扩展,在里面的命令行里粘贴执行,则不会出现这个问题😃。)

第二,粘贴的行不能出现空行,因为在命令行里相当于直接回车,会退出Python命令行模式。
执行完上面的代码,应该能看到如下结果:

让我们再做几个有趣的示例:
(2) 绘制带有标记的折线图
import matplotlib.pyplot as plt
plt.rc('lines', linewidth=2, linestyle='-', marker='*')
plt.rcParams['lines.markersize'] = 25
plt.rcParams['font.size'] = '10.0'
plt.plot([10, 20, 30, 40, 50])
plt.title("Interactive Plot")
plt.xlabel("X-axis")
plt.ylabel("Y-axis")
plt.show()

(3)绘制条形图
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.colors import Normalize
from numpy.random import rand
data = [2, 3, 5, 6, 8, 12, 7, 5]
fig, ax = plt.subplots(1, 1)
my_cmap = cm.get_cmap('jet')
my_norm = Normalize(vmin=0, vmax=8)
ax.bar(range(8), rand(8), color=my_cmap(my_norm(data)))
plt.show()

(4)绘制极坐标图
import matplotlib.pyplot as plt
import numpy as np
employee = ["Sam", "Rony", "Albert", "Chris", "Jahrum"]
actual = [45, 53, 55, 61, 57
, 45]
expected = [50, 55, 60, 65, 55, 50]
plt.figure(figsize=(10, 6))
plt.subplot(polar=True)
theta = np.linspace(0, 2 * np.pi, len(actual))
lines, labels = plt.thetagrids(range(0, 360, int(360/len(employee))), (employee))
plt.plot(theta, actual)
plt.fill(theta, actual, 'b', alpha=0.1)
plt.plot(theta, expected)
plt.legend(labels=('Actual', 'Expected'), loc=1)
plt.title("Actual vs Expected sales by Employee")
plt.show()

(5)绘制极坐标等值线图
import numpy as np
import matplotlib.pyplot as plt
# Using linspace so that the endpoint of 360 is included
actual = np.radians(np.linspace(0, 360, 20))
expected = np.arange(0, 70, 10)
r, theta = np.meshgrid(expected, actual)
values = np.random.random((actual.size, expected.size))
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
ax.contourf(theta, r, values)
plt.show()

(6)绘制热图
import matplotlib.pyplot as plt
import pandas as pd
df = pd.DataFrame([[10, 20, 30, 40], [7, 14, 21, 28], [55, 15, 8, 12],[15, 14, 1, 8]], columns=['Apple', 'Orange', 'Banana', 'Pear'],index=['Basket1', 'Basket2', 'Basket3', 'Basket4'])
plt.imshow(df, cmap="YlGnBu")
plt.colorbar()
plt.xticks(range(len(df)),df.columns, rotation=20)
plt.yticks(range(len(df)),df.index)
plt.show()

这种IDL Python互相调用的方式可以在代码中充分实现两种编程语言的强大优势。例如在IDL的pro源代码中,通过IDL创建的变量,使用Ptyhon的函数来实现变量的绘图显示,实现无缝衔接:
pro test
compile_opt idl2
;idl创建的数组变量
a=[5, 15]
b=[3, 6]
c=[8.0010, 14.2]
d=[1.95412, 6.98547, 5.41411, 5.99, 7.9999]
;转python变量
a_py = Python.Wrap(a)
b_py = Python.Wrap(b)
c_py = Python.Wrap(c)
d_py = Python.Wrap(d)
;无缝调用Python函数并绘制idl创建的变量
plt=python.Import('matplotlib.pyplot')
p=plt.plot(a_py, label='Rice', c='C7')
p=plt.plot(b_py, label='Oil', c='C8')
p=plt.plot(c_py, label='Wheat', c='C4')
p=plt.plot(d_py, label='Coffee', c='C6')
p=plt.title("Interactive Plot")
p=plt.xlabel("X-axis")
p=plt.ylabel("Y-axis")
p=plt.legend()
p=plt.show()
end
保存上面的代码为test.pro在IDL直接编译执行此代码可得到如下结果:

2. 在Python中调用IDL
在Python中同样可以调用IDL的功能。例如如下示例,直接在 Python 中调用 IDL 函数或方法,就像调用一个Python 对象一样简单:
>>> from idlpy import *
>>> arr = IDL.findgen(100)/50*3.14159
>>> x = 50*IDL.sin(arr)
>>> y = 50*IDL.cos(arr)
>>> m = IDL.map(test=1)
% Compiled module: MAP.
>>> p = IDL.plot(x - 90, y, 'r-o2', overplot=1)
% Compiled module: PLOT.
>>> p = IDL.plot(x + 90, y, 'b-o2', overplot=1)
>>> m.save('map.png', resolution=96, border=0, transparent=1)

介绍完这种无缝调用两种语言的强大之后,还是要提醒大家,混合编程虽然可以利用多种语言的优势,但对代码写作能力也提出了很高的要求。开发者需要熟悉不同语言的语法特性、交互机制以及潜在的兼容性问题,同时还要考虑代码的可维护性和性能优化。因此,建议大家根据实际情况评估需求和学习成本,合理选择是否采用混合编程方案。