本来用Tableau画3D这个系列的文章准备写三篇,但是这两天看了Alexander Varlamov的《3D Models in Tableau》的文章,这位大神把前文提到的《The 3D Full Monty》也用Excel的方式实现了,和我前面介绍的方法如出一辙。
但是大神还进一步改进了方法,利用OBJ文件的特性,提高了运行效率,并提取了颜色等信息,解决了3D模型上色的难题,我研究了很长时间,由于原文提到的Tableau Prep处理数据集的方法,我无法复现,所以就全部改用Python处理,后面会附上代码。
至于原理和方法,我就不多说了,大家可以参考原文。只简单说一下利用整理好的数据集,在实现Tableau中实现的方法。
数据集
大家可以到https://3dwarehouse.sketchup.com下载所需的3D文件,用sketchup转换成OBJ文件(或者直接网上找现成的OBJ文件),然后利用我的代码或者参考大神文章的方法,制作好所需的4个文件。
- f.csv
- v.csv
- color.csv
- g.csv
导入并整理数据
-
导入f.csv文件。
-
除FaceID以外的所有列,选中并右键选择转置,此时只剩3列。
-
加入v.csv文件。
-
选择内连接,内连接条件是f.csv文件用新建字段INT(SPLIT([转置字段值],'/',1)),v.csv用VerticID字段。
-
加入color.csv文件
-
选择内连接,连接条件是FaceID
- 如果需要,可以加入g.csv,选择内连接,连接条件也是FaceID。
开始画图
创建参数和计算字段的方法请参考用Tableau画3D模型之三(进阶篇)的文章,这里不再累述。
我做的3D钢铁侠如下图:
这里唯一的难点就是要自己去调整颜色,由于没有颜色的色值,只能自己慢慢实验。
不过要说的是,Alexander Varlamov也提到了,在Tableau中创建3D图形并非最佳选择,只是纯娱乐和学习技术,实用价值并不大。而且,像我做的这个钢铁侠的模型,也只是在某个角度效果还可以,换成其他角度,各个零件之间就会由于顺序的问题,导致错位,比如后面的零件遮挡了前面的零件,大神的文章里,针对自己的viz做了修改,由于我这个3D模型比较复杂,完全搞不清楚怎么调整,所以也就放弃了。
这就是本篇题目的由来,Tableau做3D模型确实不是它的应用场景,就目前来看仅供娱乐,当然以后会出现什么变化我们都不知道,因为Tableau总是给我们很多惊喜。
附上Public地址:https://public.tableau.com/profile/jiangbin#!/vizhome/3Dironman/1和处理数据集的代码,希望大家玩的开心。
import os
import re
import time
import pandas as pd
import numpy as np
#读取OBJ文件
with open('ironman.obj','r') as infile:
content=infile.read() #将所有stl数据读到content中
f='f (.*?)\n' #正则提取f内容
v='v (.*?)\n'
usemtl='(f|usemtl) (.*?)\n'
g='(g|f) (.*?)\n'
f_list=re.findall(f,content) #保存所有在'f'和回车之间的数据
v_list=re.findall(v,content)
usemtl_list=re.findall(usemtl,content)
g_list=re.findall(g,content)
#制作f.csv数据表格
df=pd.DataFrame(f_list) #做成表格
df=df[0].str.split(expand=True) #拆分列
df.reset_index(inplace=True) #重置index列
df.rename(columns = {'index':'FaceID'},inplace=True) #重命名列
df['FaceID']=df['FaceID']+1 #重新编号
df.to_csv('f.csv')
#制作v.csv数据表格
df=pd.DataFrame(v_list)
df=df[0].str.split(expand=True)
df.reset_index(inplace=True)
df.columns=['VerticID','x','y','z']
df['VerticID']=df['VerticID']+1
df.to_csv('v.csv')
#制作color.csv数据表格
df=pd.DataFrame(usemtl_list) #做成表格
df.reset_index(inplace=True) #重置index列
df.columns=['Rank','#','Alias'] #重命名列
def new_col1(a,b):
if 'usemtl' in a:
return b
else:
return np.NAN
df['Color']=df.apply(lambda col: new_col1(col['#'], col['Alias']),axis=1) #新建Color列
def new_col2(a,b):
if 'f' in a:
return b
else:
return np.NAN
df['Face Num Rank']=df.apply(lambda col: new_col2(col['#'], col['Rank']),axis=1) #新建Face Num Rank列
df['FaceID']=df['Face Num Rank'].rank(method='dense') #根据Face Num Rank排序,确定FaceID
df['Color']=df['Color'].fillna(method='ffill') #填充color列空值
df['FaceID'].fillna(0,inplace=True) #填充FaceID列空值
df.drop(['Face Num Rank','#','Alias','Rank'], axis=1,inplace=True) #删除辅助的列
df.to_csv('color.csv') #输出
#制作g.csv数据表格
df=pd.DataFrame(g_list)
df.reset_index(inplace=True)
df.columns=['Rank','#','Alias']
def new_col1(a,b):
if 'g' in a:
return b
else:
return np.NAN
df['Group']=df.apply(lambda col: new_col1(col['#'], col['Alias']),axis=1)
def new_col2(a,b):
if 'f' in a:
return b
else:
return np.NAN
df['Face Num Rank']=df.apply(lambda col: new_col2(col['#'], col['Rank']),axis=1)
df['FaceID']=df['Face Num Rank'].rank(method='dense')
df['Group']=df['Group'].fillna(method='ffill')
df['FaceID'].fillna(0,inplace=True)
df.drop(['Face Num Rank','#','Alias','Rank'], axis=1,inplace=True)
df.to_csv('g.csv')
print('完成')
此篇文章已发布到我的公众号:saodisir,有兴趣也可关注一下
网友评论