#+TITLE: Visualizing Perovskite Dataset
#+AUTHOR: Panayotis Manganaris
#+EMAIL: pmangana@purdue.edu
#+PROPERTY: header-args :session mrg :kernel mrg :async yes :pandoc org
* COMMENT DEV dependencies
#+INCLUDE: /home/panos/MannodiGroup/publications/Perovskites_dataset_manifest/dev_dependencies.org
#+begin_src jupyter-python :exports results :results raw drawer
  sys.path.append(os.path.expanduser("~/src/umap"))
  import umap
  ## feature engineering exploration
  from sklearn.impute import SimpleImputer
  from sklearn.preprocessing import OrdinalEncoder, Normalizer, StandardScaler
  #transformers
  from sklearn.decomposition import PCA, TruncatedSVD, KernelPCA
  from sklearn.manifold import TSNE
#+end_src

#+RESULTS:
:results:
:end:

* load data
#+INCLUDE: /home/panos/MannodiGroup/publications/Perovskites_dataset_manifest/load_full_domain.org
* preprocess data
** basis
*** create preprocessing pipeline
#+begin_src jupyter-python :exports results :results raw drawer
  # composition vectors for preprocessing
  comp_features = mm.select_dtypes(np.number).columns[
      mm.select_dtypes(np.number).columns.get_level_values(level=1).str.match(r'^[^E\a]\w{0,2}?$')
  ].to_list()

  # site-avg properties for preprocessing
  prop_features = mm.select_dtypes(np.number).columns[
      ~mm.select_dtypes(np.number).columns.get_level_values(level=1).str.match(r'^[^E\a]\w{0,2}?$')
  ].to_list()

  # categorical properties for preprocessing
  cat_features = mm.select_dtypes('object').columns.to_list()

  # define preprocessing
  fillna = SimpleImputer(strategy="constant", fill_value=0.0)
  mkratio = Normalizer(norm='l1')
  mknormal = StandardScaler()
  mkbound = MinMaxScaler(feature_range=(0,1), clip=False) #not statistical, should work for anything

  comp_transformer = mkpipe(fillna, mkratio, mkbound)
  prop_transformer = mkpipe(mknormal, mkbound)
  cat_transformer = ohe(handle_unknown="ignore")

  preprocessor = colt(
      transformers=[
          ("comp", comp_transformer, comp_features),
          ("prop", prop_transformer, prop_features),
          ("cat", cat_transformer, cat_features),
      ]
  )
#+end_src

#+RESULTS:
:results:
:end:

*** fit data and create visualization df
#+begin_src jupyter-python :exports results :results raw drawer
  preprocessor.fit(mm)

  X = pd.DataFrame(preprocessor.transform(mm),
                   columns=preprocessor.get_feature_names_out(mm.columns.to_list()),
                   index=mm.index)
#+end_src

#+RESULTS:
:results:
:end:

** targets
no target transformations yet
* Target space
properties collected from DFT computations using the PBE and HSE
functionals can be explored here.

These properties are targets for modeling. Ideally, an empirical model
can be found that fits to the underlying quantum mechanics, thereby
acting as a surrogate for the DFT function in an active learning
strategy which can quickly recommend compositions as high-performing
candidates for further DFT calculation.

The target space is briefly summarized in both uni-variate and bi-variate views

#+begin_src jupyter-python :exports results :results none
  Y = my.select_dtypes(np.number).filter(regex=r'^(?!efficiency|.*dbg)', axis=1).dropna(axis=1, how='all')
#+end_src
  
ploty.js is being extended to enable improved sploms
#+begin_src jupyter-python :exports results :results raw drawer :file ./domains/PBE_pairplot_2.png
  p = px.scatter_matrix(Y,
                        dimensions=Y.select_dtypes(np.number).columns,
                        color=mix[my.LoT=='PBE'],
                        template='simple_white',
                        width=800, height=800)
  p.update_layout(
      # paper_bgcolor='rgba(255,255,255,0)',
      # plot_bgcolor='rgba(255,255,255,0)',
      legend=dict(itemsizing='constant')
  )
  p.update_traces(marker=dict(size=2), diagonal=dict(visible=False))
  # p.update_xaxes(showline=True, linewidth=2, linecolor='black', ticks='outside', showgrid=False)
  # p.update_yaxes(showline=True, linewidth=2, linecolor='black', ticks='outside', showgrid=False)
  p.show(renderer='png')
  p.write_html('./test.html')
#+end_src

#+begin_src jupyter-python :exports results :results raw drawer :file ./domains/PBE_pairplot_3.png
  import plotly.figure_factory as ff
  Y1 = Y.assign(mix=mix[my.LoT=='PBE'])
  p = ff.create_scatterplotmatrix(Y1, diag='histogram',
                                  index='mix', size=2,
                                  #template='seaborn',
                                  width=800, height=800)
  p.update_layout(
      paper_bgcolor='rgba(255,255,255,0)',
      plot_bgcolor='rgba(255,255,255,0)',
      legend=dict(itemsizing='constant')
  )
  p.update_xaxes(showline=True, linewidth=2, linecolor='black', ticks='outside', showgrid=False, zeroline=False)
  p.update_yaxes(showline=True, linewidth=2, linecolor='black', ticks='outside', showgrid=False, zeroline=False)
  #p.update_traces()
  p.show(renderer='png')
#+end_src

* TODO Pie Charts
TODO: remove labels for convenience

How datasets under consideration distribute their sample of the alloy axes
** make and label shares
#+begin_src jupyter-python :exports results :results raw drawer
  PBE_comp = mc
#+end_src

*** alloy shares
#+begin_src jupyter-python :exports results :results raw drawer
  PBE_alloy_group = PBE_comp.groupby(level=["mix"])
  HSE_alloy_group = HSE_comp.groupby(level=["mix"])
  PBE_alloy_share = PBE_alloy_group.apply(len)
  PBE_alloy_share.name=""
  HSE_alloy_share = HSE_alloy_group.apply(len)
  HSE_alloy_share.name=""
#+end_src

*** total comp
#+begin_src jupyter-python :exports results :results raw drawer :pandoc org
  PBE_total = PBE_comp.count()
  PBE_total.name = "Total"
  HSE_total = HSE_comp.count()
  HSE_total.name = "Total"
#+end_src

*** comp per group
#+begin_src jupyter-python :exports results :results raw drawer :pandoc org
  PBE_total_group = PBE_alloy_group.count()
  HSE_total_group = HSE_alloy_group.count()
#+end_src

** plot shares
*** define plot functions
#+begin_src jupyter-python :exports results :results raw drawer
  mydpi=96
  titlefont = {'family': 'Arial', 'color': 'black', 'weight': 'bold', 'size': 32}
  titlefont2 = {'family': 'Arial', 'weight': 'bold', 'size': 17}
  labelfont = {'family': 'Arial', 'color': 'black', 'weight': 'normal', 'size': 30}
  annotfont = {'family': 'Arial', 'color': 'black', 'weight': 'normal', 'size': 20}
  annotfont2 = {'family': 'Arial', 'color': 'black', 'weight': 'normal', 'size': 14}

  def absolute_value(val, series):
      a  = np.round(val/100.*series.values.sum(), 0)
      return int(a)

  def plot_alloy_shares(df):
      dfav = partial(absolute_value, series = df)
      fig, ax = plt.subplots(1,1, figsize=(800/mydpi, 800/mydpi), dpi=mydpi)
      ax = df.plot.pie(ax=ax, autopct=dfav, textprops=annotfont)
      ax.set_title("Alloy Representation", fontdict=titlefont)
      ax.set_xlabel("", fontdict=labelfont)
      ax.set_ylabel("")
      return fig

  def plot_const_shares(df):
      fig, ax = plt.subplots(1,1, figsize=(800/mydpi, 800/mydpi), dpi=mydpi)
      ax = df.plot.pie(ax=ax, autopct=lambda x: f"{x:.2f}" + "%", pctdistance=0.8, textprops=annotfont)
      ax.set_title("Constituent Representation", fontdict=titlefont)
      ax.set_xlabel(ax.get_ylabel(), fontdict=labelfont)
      ax.set_ylabel("")
      return fig

  def plot_const_per_alloy(df):
      fig, axar = plt.subplots(2,2, figsize=(1200/mydpi, 1200/mydpi), dpi=mydpi)
      axar = df.plot.pie(ax=axar, subplots=True, autopct=lambda x: f"{x:.2f}" + "%",
                         pctdistance=0.8, radius=1.05, legend=False, textprops=annotfont2)
      for ax in axar:
          ax.set_xlabel(ax.get_ylabel(), fontdict=labelfont)
          ax.set_ylabel("")
      fig.tight_layout()
      fig.suptitle("Constituent Representation\nby Alloy Scheme")
      return fig
#+end_src

*** PBE
#+begin_src jupyter-python :exports results :results raw drawer
  fig1 = plot_alloy_shares(PBE_alloy_share)
  fig2 = plot_const_shares(PBE_total)
  fig3 = plot_const_per_alloy(PBE_total_group.T) #.drop("BandX", axis=1)

  fig1.show()
  fig2.show()
  fig3.show()
#+end_src

#+begin_src jupyter-python :exports results :results raw drawer
  fig1.savefig("./PBE_Alloy_Representations.png", dpi = mydpi, transparent=True)
  fig2.savefig("./PBE_Constituent_Representations.png", dpi = mydpi, transparent=True)
  fig3.savefig("./PBE_Constituent_Representations_per_Scheme.png", dpi = mydpi, transparent=True)

  plt.show()
#+end_src

*** HSE
#+begin_src jupyter-python :exports results :results raw drawer
  fig1 = plot_alloy_shares(HSE_alloy_share)
  fig2 = plot_const_shares(HSE_total)
  fig3 = plot_const_per_alloy(HSE_total_group.T)

  fig1.show()
  fig2.show()
  fig3.show()
#+end_src

#+begin_src jupyter-python :exports results :results raw drawer
  fig1.savefig("./HSE_Alloy_Representations.png", dpi = mydpi, transparent=True)
  fig2.savefig("./HSE_Constituent_Representations.png", dpi = mydpi, transparent=True)
  fig3.savefig("./HSE_Constituent_Representations_per_Scheme.png", dpi = mydpi, transparent=True)

  plt.show()
#+end_src

* TODO Sample Space
show sparse alloy space coverage
#+begin_src jupyter-python :exports none :results raw drawer     
  rownames = [zip(comptuple, Cl_ABmixing_decomp.index.names) for comptuple in Cl_ABmixing_decomp.index.to_flat_index()]
  rownames = [list(labelzip) for labelzip in rownames]
  colnames = [zip(comptuple, Cl_ABmixing_decomp.columns.names) for comptuple in Cl_ABmixing_decomp.columns.to_flat_index()]
  colnames = [list(labelzip) for labelzip in colnames]
  %matplotlib inline
  Cl_array_AorBmix = Cl_ABmixing_decomp.to_numpy()
  fig, ax = plt.subplots(figsize=[30,30])
  im, cbar = heatmap(Cl_array_AorBmix, rownames[::-1], colnames[::-1], ax=ax,
                     cmap="magma_r", cbarlabel="PBE Decomp E [eV p.f.u]")
  cbar.ax.tick_params(labelsize=35)
  cbar.set_label(label = cbar.ax.get_title(), fontsize=40)
  texts = annotate_heatmap(im, valfmt="{x:.1f} eV", fontsize='xx-small')
  fig.tight_layout()
  ax.set_title('PBE Predicted Decomp E for Cl X-site Perovskites\n composed of 5 A-site and 6 B-site candidates in N fractions of 8',
               fontsize = 40)  
  plt.show()
#+end_src

* DONE Feature space
:STATUSLOG:
- State "DONE"       from "TODO"       [2022-07-11 Mon 03:46]
:END:
** Composition Distributions
composition vectors are a set of primary descriptors for the
Perovskites being examined -- most other meaningful features are at
least partially derived from them. Another primary descriptor is the
crystal structure. For now, it is understood that the 496 records
being examined are all cubic perovskites (within a tolerance). They
differ firstly in composition and secondly in alloy character. Alloy
character as a metric is completely encapsulated in the composition
vectors, but nonetheless represents an important consideration in
ensuring the model's generality.

It will be a goal of modeling to create regressions that will be able
to extrapolate targets between the existing alloy character classes.
(e.g. AandBandX-site alloys).

Here, uni-variate distributions over finite bounds on composition
ratios are explored with respect to the alloy class.

#+begin_src jupyter-python :exports results :results raw drawer
  Xc = X.iloc[
      :, strings_where(np.array(comp_features), X.columns.to_numpy())
  ].iloc[:,:-3].assign(mix=mix).assign(org=org)

  Xcm = pd.melt(Xc, id_vars=["mix", "org"]).replace(0, np.NaN).dropna()
  # eliminate the "zeros" (missing values) to focus on the meaningful data
#+end_src

#+RESULTS:
:results:
:end:

#+begin_src jupyter-python :exports results :results raw drawer
  with sns.plotting_context("poster"):
      p = sns.catplot(x="value", col="element", data=Xcm, col_wrap=5, kind="count", hue="mix",
                      col_order=["Ba", "Ge", "Cl", "Br", "I", "Sn", "Pb", "Cs", "FA", "MA", "Sr", "Ca", "Rb", "K"])
      (p.set_xticklabels(rotation=90))
      sns.move_legend(p, bbox_to_anchor=(0.85, 0.15), loc="center")
#+end_src

#+begin_src jupyter-python :exports results :results raw drawer :file ./domains/variability_of_composition_vectors2.png
  p = px.histogram(Xcm, x="value", facet_col='variable', facet_col_wrap=5, color="mix", template='seaborn',
                   #category_orders={'variable':["Ba", "Ge", "Cl", "Br", "I", "Sn", "Pb", "Cs", "FA", "MA", "Sr", "Ca", "Rb", "K"]}
                   barmode='group')
  p.update_layout(
      paper_bgcolor='rgba(255,255,255,0)',
      plot_bgcolor='rgba(255,255,255,0)',
      legend=dict(itemsizing='constant')
  )
  p.for_each_annotation(lambda a: a.update(text="".join(a.text[16:-1].split("'"))))
  p.update_xaxes(showline=True, linewidth=2, linecolor='black', ticks='outside', showgrid=False)
  p.update_yaxes(showline=True, linewidth=2, linecolor='black', ticks='outside', showgrid=False)
  p.show(renderer="png")
#+end_src

#+RESULTS:
:results:
#+attr_org: :width 700
[[file:./domains/variability_of_composition_vectors2.png]]
:end:

** Site-Averaged Properties Distributions 
#+begin_src jupyter-python :exports results :results raw drawer
  Xp = X.iloc[
      :, strings_where(np.array(prop_features), X.columns.to_numpy())
  ].assign(mix=mix).assign(org=org)

  Xpm = pd.melt(Xp, id_vars=["mix", "org"]).replace(0, np.NaN).dropna()
  # eliminate the "zeros" (missing values) to focus on the meaningful data
#+end_src

#+RESULTS:
:results:
:end:

#+begin_src jupyter-python :exports results :results raw drawer
  with sns.plotting_context("notebook"):
      p = sns.displot(x="value", col=smp.iloc[:,3], row="site", data=Xpm, kind="hist", hue="mix", multiple='stack')
#+end_src

#+begin_src jupyter-python :exports results :results raw drawer :file ./domains/variability_of_site_avg_prop_vectors.png
  p = px.histogram(Xpm, x="value", facet_col='variable', facet_col_wrap=6, color="mix", template='seaborn',
                   barmode='overlay', width=1000, height=1000)
  p.update_layout(
      # paper_bgcolor='rgba(255,255,255,0)',
      # plot_bgcolor='rgba(255,255,255,0)',
      legend=dict(itemsizing='constant')
  )
  p.for_each_annotation(lambda a: a.update(text="".join(a.text.split("'")[1:-1])))
  p.update_xaxes(showline=True, linewidth=2, linecolor='black', ticks='outside', showgrid=False)
  p.update_yaxes(showline=True, linewidth=2, linecolor='black', ticks='outside', showgrid=False)
  p.show(renderer="png")
#+end_src

#+RESULTS:
:results:
[[file:./domains/variability_of_site_avg_prop_vectors.png]]
:end:

* Notes on Multivariate relations
:PROPERTIES:
:ID:       117889c3-28f7-4266-9fe2-8ae3a849cb3a
:END:
To get a better idea of what structures statistical models might be
able to find in the complete dataset, the structure and effects of
many variables at a time must be inspected.

Principal Component Analysis is a method of projecting high
dimensional data onto a plane defined by the two linear combinations
of axes that explain as much of the variance as possible.

This PCA is performed by computing the Singular Value Decomposition, a
Unitary Transform which generalizes the familiar
eigendecomposition. Essentially, the data cloud is "rotated" in m-D
space until their widest 2D cross-section is visible.

Various projections are generated
- the PBE and HSE target spaces are projected independently. No
  obvious clustering is observed with respect to alloy class.
- the composition vector space is projected. As expected from the
  variably histograms, the projection finds the most variability in a
  linear combination of the X-site axes.
  - obvious clustering is seen with respect to alloy class, but there
    is no helpful correlation with a target property.
- the site-averaged property vector space is more ambiguous. However,
  some topology is still evident. Again, X-site alloy compounds appear
  to constitute most of the "spread" in the projection, with other mixed 
* PCA
:PROPERTIES:
:ID:       693a9e96-3d0d-489a-a178-656687b5ac6c
:END:
#+begin_src jupyter-python :exports results :results raw drawer
  pcaxis = PCA(n_components = min(Xc.select_dtypes(np.number).shape), svd_solver = 'full')
  pcaxis.fit(Xc.select_dtypes(np.number))
#+end_src

#+RESULTS:
:results:
: PCA(n_components=14, svd_solver='full')
:end:

#+begin_src jupyter-python :exports results :results raw drawer :file ./multivar/comp_ratio_bound_projection.png
  p, pcadata = biplot(pcaxis=pcaxis, data=Xc.select_dtypes(np.number), x=0, y=1, color=Xc.mix)
  p.show(renderer='png')
#+end_src

#+RESULTS:
:results:
#+attr_org: :width 700
[[file:./multivar/comp_ratio_bound_projection.png]]
:end:

At this point it is readily apparent that this dataset is highly
topological. The data exists on a mostly bounded domain in high
dimensions, so there is some geometry the features constitute.

Our models will prefer to use this geometric structure in their
explanation of Perovskite variation, this can be useful for accuracy,
it can also be a bias-inducing hindrance.
* Kernel PCA
#+begin_src jupyter-python :exports results :results raw drawer
  kpcaxis = KernelPCA(n_components=min(Xc.select_dtypes(np.number).shape),
                      kernel="rbf", gamma=0.6, fit_inverse_transform=True, alpha=0.1)
  kpcaxis.fit(Xc.select_dtypes(np.number))
#+end_src

#+RESULTS:
:results:
: KernelPCA(alpha=0.1, fit_inverse_transform=True, gamma=0.6, kernel='rbf',
:           n_components=14)
:end:
  
#+begin_src jupyter-python :exports results :results raw drawer
  kdf = pd.DataFrame(
      kpcaxis.transform(Xc.select_dtypes(np.number)),
      index=Xc.index,
      columns=kpcaxis.get_feature_names_out()
  )
#+end_src

#+RESULTS:
:results:
:end:

#+begin_src jupyter-python :exports results :results raw drawer
  p = px.scatter(kdf, x='kernelpca0', y='kernelpca1', color=Xc.mix)
  p.show(renderer='png')
#+end_src

#+RESULTS:
:results:
#+attr_org: :width 700
[[file:./.ob-jupyter/41d1c805330cbe31a8f4adce403223dfb1487e8c.png]]
:end:

* Truncated SVD
A basic method for factor analysis
#+begin_src jupyter-python :exports results :results none
  svdaxis = TruncatedSVD(n_components=2, n_iter=15)
  svdaxis.fit(df)
#+end_src

#+begin_src jupyter-python :exports results :results raw drawer
  svdf = pd.DataFrame(
      svdaxis.transform(df),
      index=df.index,
      columns=[f'pc_{i}' for i in range(svdaxis.n_components)]
  )
#+end_src

#+begin_src jupyter-python :exports results :results raw drawer
  p = sns.scatterplot(data=svdf, x='pc_0', y='pc_1', style='mix', hue='org')
  p.figure.show()
#+end_src

* TSNE
tSNE method initializes itself using the PCA transformation of the fit
data. alternative initilizations can be passed manually. Or a random
initialization may be used.

tSNE metric arg defines method for determining distances between
instances in the feature array.
#+begin_src jupyter-python :exports both :results raw drawer
  perplexities = [5, 10, 15, 25, 30, 35, 50, 100]

  tsnedf_list = []
  for perp in perplexities:
      tsne = TSNE(n_components=2, perplexity=perp,
                  init="pca", metric='hamming',
                  learning_rate=200, random_state=1)
      tsnedf = pd.DataFrame(
          tsne.fit_transform(Xc.select_dtypes(np.number)),
          index = Xc.index,
          columns = [f'{i}' for i in range(tsne.n_components)]
      ).assign(perplexity=perp).assign(mix=mix).assign(org=org).assign(formula=my.Formula)

      tsnedf_list.append(tsnedf)

  tsnedf = pd.concat(tsnedf_list, axis=0)
#+end_src

#+RESULTS:
:results:
#+begin_example
  /opt/miniconda3/envs/mrg/lib/python3.10/site-packages/sklearn/manifold/_t_sne.py:991: FutureWarning:

  The PCA initialization in TSNE will change to have the standard deviation of PC1 equal to 1e-4 in 1.2. This will ensure better convergence.

  /opt/miniconda3/envs/mrg/lib/python3.10/site-packages/sklearn/manifold/_t_sne.py:991: FutureWarning:

  The PCA initialization in TSNE will change to have the standard deviation of PC1 equal to 1e-4 in 1.2. This will ensure better convergence.
  /opt/miniconda3/envs/mrg/lib/python3.10/site-packages/sklearn/manifold/_t_sne.py:991: FutureWarning:

  The PCA initialization in TSNE will change to have the standard deviation of PC1 equal to 1e-4 in 1.2. This will ensure better convergence.
  /opt/miniconda3/envs/mrg/lib/python3.10/site-packages/sklearn/manifold/_t_sne.py:991: FutureWarning:

  The PCA initialization in TSNE will change to have the standard deviation of PC1 equal to 1e-4 in 1.2. This will ensure better convergence.
  /opt/miniconda3/envs/mrg/lib/python3.10/site-packages/sklearn/manifold/_t_sne.py:991: FutureWarning:

  The PCA initialization in TSNE will change to have the standard deviation of PC1 equal to 1e-4 in 1.2. This will ensure better convergence.
  /opt/miniconda3/envs/mrg/lib/python3.10/site-packages/sklearn/manifold/_t_sne.py:991: FutureWarning:

  The PCA initialization in TSNE will change to have the standard deviation of PC1 equal to 1e-4 in 1.2. This will ensure better convergence.
  /opt/miniconda3/envs/mrg/lib/python3.10/site-packages/sklearn/manifold/_t_sne.py:991: FutureWarning:

  The PCA initialization in TSNE will change to have the standard deviation of PC1 equal to 1e-4 in 1.2. This will ensure better convergence.
  /opt/miniconda3/envs/mrg/lib/python3.10/site-packages/sklearn/manifold/_t_sne.py:991: FutureWarning:

  The PCA initialization in TSNE will change to have the standard deviation of PC1 equal to 1e-4 in 1.2. This will ensure better convergence.
#+end_example
:end:

#+begin_src jupyter-python :exports both :results raw drawer :file ./multivar/tsne_comp_bound_DecoE_clusters.png
  p = px.scatter(tsnedf,
                 facet_col='perplexity', facet_col_wrap=4,
                 x='0', y='1',
                 color=Y.DecoE_eV.to_list()*8, hover_name=tsnedf.formula)
  p.update_layout(coloraxis=dict(colorbar=dict(title="Decomposition Energy [eV]", title_side='right')))
  p.update_yaxes(matches=None, visible=False)
  p.update_xaxes(matches=None, visible=False)
  p.show(renderer='png')
#+end_src

#+RESULTS:
:results:
#+attr_org: :width 700
[[file:./multivar/tsne_comp_bound_DecoE_clusters.png]]
:end:

#+begin_src jupyter-python :exports both :results raw drawer
  p = sns.relplot(data=tsnedf, col='perplexity', col_wrap=4, x='e_0', y='e_1', hue=pd.concat([my.dielc]*8, axis=0), palette='magma', style='mix',
                  height=3.0, aspect=1.0,
                  facet_kws=dict(sharex=False, sharey=False))
  p.figure.show()
#+end_src

#+begin_src jupyter-python :exports both :results raw drawer
  p.figure.savefig('./tsne_comp_DecoE_clusters', transparent=True)
#+end_src

** DecoE clustering
- hamming distance
- pca init
- perplexity 50

* UMAP
#+begin_src jupyter-python :exports both :results raw drawer
  perplexities = [15]

  umapdf_list = []
  for perp in perplexities:
      mapper = umap.UMAP(n_neighbors=perp,
                         min_dist=0.1,
                         metric='euclidean')
      umapdf = pd.DataFrame(
          mapper.fit_transform(Xc.select_dtypes(np.number)),
          index = Xc.index,
          columns = [f'm_{i}' for i in range(mapper.n_components)]
      ).assign(perplexity=perp).assign(mix=mix).assign(org=org).assign(formula=my.Formula)

      umapdf_list.append(umapdf)

  umapdf = pd.concat(umapdf_list, axis=0)
#+end_src

#+RESULTS:
:results:
:end:

#+begin_src jupyter-python :exports both :results raw drawer 
  p = px.scatter(umapdf,
                 facet_col='perplexity', facet_col_wrap=4,
                 x='m_0', y='m_1',
                 color='org',
                 symbol='mix',
                 hover_name="formula")
  p.update_yaxes(matches=None,
                 scaleanchor="x", )
  p.update_xaxes(matches=None)
  p.show(renderer='png')
#+end_src

#+RESULTS:
:results:
#+attr_org: :width 700
[[file:./.ob-jupyter/c3bb81765be2211bc707e296ae82c5e5b1cffac8.png]]
:end:

* TODO
- slide on GNNs
- all figures all ways
  - paper
  - proposal contents
* reference
bibliographystyle:plain
bibliography:~/org/bibliotex/bibliotex.bib
