# Set PCA loadings (aka arrows) in a 3d Scatter plot

**URL:** <https://community.plotly.com/t/set-pca-loadings-aka-arrows-in-a-3d-scatter-plot/72905>\
**Category:** 📊 Plotly Python\
**Tags:** question\
**Created:** [February 21, 2023, 10:59am UTC](https://community.plotly.com/t/set-pca-loadings-aka-arrows-in-a-3d-scatter-plot/72905 "2023-02-21T10:59:37Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Mirk0\_98](https://avatars.discourse-cdn.com/v4/letter/m/3e96dc/32.png) [@Mirk0\_98](https://community.plotly.com/u/Mirk0_98)\
**Post date:** [February 21, 2023, 10:59am UTC](https://community.plotly.com/t/set-pca-loadings-aka-arrows-in-a-3d-scatter-plot/72905/1 "2023-02-21T10:59:37Z")

</div>

I’m trying to plot a PCA in 3D.  
For those who don’t know a PCA is simply plotted as a scatterplot and annotated with arrows that represents some feature of the analyzed objects, with different lengths based on how important that feature is.

This is a 2D example

```auto

import numpy as np
import pandas as pd
from scipy.stats import norm
import plotly.express as px
import plotly.graph_objects as go

def make_pca_plot(feature_df, features,  
                  arrowsize = 1,
                  arrowhead = 1,
                  arrowscale = 6,
                  ):

    pca = PCA()
    pca.fit(feature_df[features])
    components = pca.fit_transform(feature_df[features])
    loadings = pca.components_.T * np.sqrt(pca.explained_variance_)

    fig = px.scatter(components, x=0, y=1)

    for i, feature in enumerate(features):
        fig.add_annotation(
            ax=0, ay=0,
            axref="x", ayref="y",
            x=loadings[i, 0]*arrowscale,
            y=loadings[i, 1]*arrowscale,
            showarrow=True,
            arrowsize=arrowsize,
            arrowhead=arrowhead,
            xanchor="right",
            yanchor="top"
        )
        fig.add_annotation(
            x=loadings[i, 0]*arrowscale,
            y=loadings[i, 1]*arrowscale,
            ax=0, ay=0,
            xanchor="center",
            yanchor="bottom",
            text=feature,
            yshift=5,
        )
    fig.update_layout(title='Total explained variance PC1+PC2: {}'.format(round(pca.explained_variance_ratio_[0:2].cumsum()[-1],2)))
    return fig

np.random.seed(10)
testdf = pd.DataFrame({
    'A' : np.random.rand(100), 
    'B' : norm(0,1).rvs(100),
    'C' : norm(2,1).rvs(100),
    'D' : norm(2,1).rvs(100),
})

make_pca_plot(testdf, testdf.columns)

```

 ![image](https://us1.discourse-cdn.com/flex024/uploads/plot/original/3X/a/3/a3c1f71cc9f2fbb65aa276e911a3ad07cd206c14.png)

As you can see there are arrows that start from the origin of the axes and end in set position depending on the `loading` variable value for that feature.

I want to obtain the same result in a 3D scatter plot, but I can’t manage to do that. Specifically i do not understand how to set the start of the arrow on the origin.

So far I obtained this

```auto
def make_3D_pca_plot(feature_df, features,  
                  arrowsize = 1,
                  arrowhead = 1,
                  arrowscale = 6,
                  ):

    pca = PCA()
    pca.fit(feature_df[features])
    components = pca.fit_transform(feature_df[features])
    loadings = pca.components_.T * np.sqrt(pca.explained_variance_)

    fig = px.scatter_3d(components, x=0, y=1, z=2)

    fig.update_layout(
        scene = dict(
            annotations=[
                dict(
                    # ax=0, ay=0, 
                    showarrow = True,
                    arrowsize=arrowsize,
                    arrowhead=arrowhead,
                    x = loadings[i, 0]*arrowscale,
                    y = loadings[i, 1]*arrowscale,
                    z = loadings[i, 2]*arrowscale,
                    xanchor="center",
                    yanchor="bottom",
                    text = feature,
                    yshift=5,
                )
            for i, feature in enumerate(features)]
        )
    )
    fig.update_layout(title='Total explained variance PC 1+2+3: {}'.format(round(pca.explained_variance_ratio_[0:3].cumsum()[-1],2)))
    return fig

make_3D_pca_plot(testdf, testdf.columns[0:3],  
                  arrowscale=4
                  )

```

![image](https://us1.discourse-cdn.com/flex024/uploads/plot/original/3X/0/7/07c03f658475115630298f0710263f614ab6ad5b.png)

if i uncomment the `ax` or `ay` parameter the arrows disappear.  
Also you can tweak `arrowscale` to see what is happening to the annotations.

---

<div class="post-metadata">

**Author:** ![AIMPED](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/aimped/32/21758_2.png) [@AIMPED](https://community.plotly.com/u/AIMPED)\
**Post date:** [February 21, 2023, 9:01pm UTC](https://community.plotly.com/t/set-pca-loadings-aka-arrows-in-a-3d-scatter-plot/72905/2 "2023-02-21T21:01:32Z")

</div>

HI @Mirk0_98, I actually never used annotitions in 3D. Does the example `3D Annotations` help?

> **[Text](https://plotly.com/python/text-and-annotations/)**
>
> Over 17 examples of Text and Annotations including changing color, size, log axes, and more in Python.

Instead of using annotations you could use additional scatter3d traces. Here I did this in 2D but it’s going to be pretty similar in 3D:

> [@Plot arrow ( given direction - angle) at given coordinates](https://community.plotly.com/t/plot-arrow-given-direction-angle-at-given-coordinates/68500/2):
>
> Hi @yogi_dhiman , if you do not mind the circles instead of arrowheads, you could do something like this: import plotly.graph\_objects as go import numpy as np # set data x = np.asarray([0,1,2,3,4,5,6]) y = np.asarray([0,1,2,3,4,5,6]) a = np.asarray([55,-20,34,22,76,-20,-80]) length = 1 # calculate coordinates of second vector point dx = x + np.cos(np.radians(a)) \* length dy = y + np.sin(np.radians(a)) \* length # add start and end points to array xx = np.c\_[x, dx] yy = np.c\_[y, dy] # create…
