本文已参与【新人创作礼】活动,一起开启掘金创作之路
从PlatEMO中提取真实PF前沿
觉得有用的话,欢迎一起讨论相互学习~
- 众所周知,我是Jmetal的重度爱好者,最近实验遇到一些难以解决的困难,当我在进行超多目标优化实验即MaOP时,需要M=10及以上的PF,然而在benchmark中没有提供,而且Jmetal不支持通过均匀取点的方式生成PF。因此,经过老师的指导,我们选择使用在PlatEMO中运行完相应目标数量的benchmark problem后,将通过均匀踩点得到的真实PF提取出来作为在Jmetal上进行实验的真实PF.
观察platEMO中PF数据结构
- 首先进入Test模块,选择好自己的算例和目标数量,此处设置目标数量为10,然后使其继续运行,直到完成迭代次数。
- 选择data-source模式为True PF,此时图中显示的在此目标数量下test problem的真实前沿
- 此时我们获得了真实前沿,但是还需要其中的散点数据,只有将这些数据保存出来,才能在Jmetal中画出前沿
- 此时选中-->open in new figure and save to workspace
- 即会出现这样的小窗口,在主窗口选择打开变量Data{},其中打开的即是对应图形中真实PF的信息。
- 点开第一个单元格,可以看到数据的保存格式,表示平行坐标图的横轴,是从1-10然后从10-1的不断重复的序列,第二个单元格中存储的是对应的目标函数值
- 因此为了将其转换成Jemtal可使用的标准PF形式,即每一列表示一个目标,每一行表示一个PF上的点。需要将目前的数据格式做如下的处理,即
- 将每十列重新分为一行
- 单数行索引顺序保持不变,双数行索引顺序倒置
准备处理数据
- 新建一个excel表格保存数据,注意,如果直接将一整行进行保存,可能出现excel中列不够的情况,因此需要先将矩阵在platEMO中转置后变成一列再复制到Excel中。由于目标数是10是固定的,因此只需要对目标对应的函数值进行处理。
- 嗯,现在再对数据做第一个处理,将数据每十行变成一列
使用excel公式
=INDEX($A:$A,ROW(A1)*10-10+COLUMN(A1))在单元格选中,然后向右拖10行,然后选中行,向下拉满 处理好后的数据如图所示: - 但是其双数行还是从10-1的目标索引进行排列,为了保持一致,其双数行需要变成从1-10的目标索引进行排列
对双数行进行处理
-
因为现在双数行的索引模式是倒序的,因此需要将其变为顺序模式,为此,首先将数据复制一遍,成为没有公式的纯数据。
-
然后将其保存为csv文件格式,方便之后进行处理,excel另存为csv(逗号分割)。
-
写一个python 脚本对
test1.csv文件进行处理。
import csv
# 源文件名
filename = "./test1.csv"
pf_file = "./test1.pf"
singular_line = [] # 单数行
double_line = [] # 双数行
num_obj = 10 # 目标个数,这表示其中列的个数
singular_data = [[] for i in range(num_obj)] # 单数行保存为一个num_obj列的二维数组
double_data = [[] for i in range(num_obj)] # 单数行保存为一个num_obj列的二维数组
# 将文件中的信息保存到singular_data和double_data这两个列表中
with open(filename, 'r', encoding='utf-8') as f:
i = 1
for line in f.readlines():
if i % 2 == 0:
double_line.append(line)
else:
singular_line.append(line)
i = i + 1
f.close()
# 再将两个列表中的数据进行处理,将双数行中的数据换顺序保存
# 单数行直接保存
for Sum in singular_line:
# print(Sum.split("\n")[0].split(",")[0:])
sum_eva_index = Sum.split("\n")[0].split(",")[0:]
for i in range(num_obj):
# print(sum_eva_index[i]) # test
singular_data[i].append(float(sum_eva_index[i]))
# 双数行反向后保存
for Sum in double_line:
sum_eva_index = Sum.split("\n")[0].split(",")[0:]
for i in range(num_obj):
# print("i", i)
double_data[i].append(float(sum_eva_index[num_obj - i - 1])) # 注意,即使num_obj是目标数量,但是索引是从num_obj-1开始的
# 将数据写入CSV文件中
# 将数据写入csv日志文件中
with open(pf_file, 'w') as f:
for i in range(len(singular_data[0])): # 3504
for j in range(len(singular_data)): # 10
# f.write([singular_data[0][i], singular_data[1][i], singular_data[2][i], singular_data[3][i],
# singular_data[4][i], singular_data[5][i], singular_data[6][i], singular_data[7][i],
# singular_data[8][i], singular_data[9][i]])
f.write(str(singular_data[j][i]))
# 用制表符做分割
if j!=(len(singular_data)-1):
f.write("\t")
# 换行符
f.write("\n")
for i in range(len(double_data[0])): # 3504
for j in range(len(double_data)): # 10
f.write(str(double_data[j][i]))
# 用制表符做分割
if j!=(len(double_data)-1):
f.write("\t")
# 换行符
f.write("\n")
f.close()
# with open(pf_file, "w", newline='') as f:
# writer = csv.writer(f)
#
# # print(len(singular_data)) # 10
# # print(len(singular_data[0])) # 3504
# # 遍历行
# for i in range(len(singular_data[0])): # i 取(0,1,2...)
# writer.writerow([singular_data[0][i], singular_data[1][i], singular_data[2][i], singular_data[3][i],
# singular_data[4][i], singular_data[5][i], singular_data[6][i], singular_data[7][i],
# singular_data[8][i], singular_data[9][i]])
#
# # 再写双数行
# for i in range(len(double_data[0])): # i 取(0,1,2...)
# writer.writerow([double_data[0][i], double_data[1][i], double_data[2][i], double_data[3][i],
# double_data[4][i], double_data[5][i], double_data[6][i], double_data[7][i],
# double_data[8][i], double_data[9][i]])
#
# f.close()
最终结果
- 可以来看看最终结果,这里使用30行数据为例,test1.csv单数行被改到test1.pf的前15行,而转换顺序后的test1.csv的双数行被改到test1.pf的后15行。