# How to put the variable names of pandas data frame on Diagonal of Seaborn PairGrid?

The given below code is not working now(earlier it was working) to put the column names on the diagonal of Seaborn PairGrid.

import matplotlib.pyplot as plt
import seaborn as sns

def diagfunc(x, **kws):
ax = plt.gca()
ax.annotate(x.name, xy=(0.05, 0.9), xycoords=ax.transAxes)

sns.PairGrid(iris).map_diag(diagfunc)


At present it showing this error!

<ipython-input-137-f4d9b71087cb> in diagfunc(x, **kws)
5 def diagfunc(x, **kws):
6     ax = plt.gca()
----> 7     ax.annotate(x.name, xy=(0.05, 0.9), xycoords=ax.transAxes)
8
9 sns.PairGrid(iris).map_diag(diagfunc)

AttributeError: 'numpy.ndarray' object has no attribute 'name'


Can anyone help me how to put the column name in diagonal?

With minimal changes, I got this to work (python3.7.3, SeaBorn 0.9.0):

import matplotlib.pyplot as plt
import seaborn as sns

next_iris_label = iter(iris).__next__

def diagfunc(x, **kws):
ax = plt.gca()
#  replace x.name with next_iris_label call
ax.annotate(next_iris_label(), xy=(0.05, 0.9), xycoords=ax.transAxes)

sns.PairGrid(iris).map_diag(diagfunc)


Output:

I dug into it and understood that the reason this fails is because once the IRIS dataset has been passed to SeaBorn.PairGrid, the original Names of observation columns are not retained. map_diag ends up calling diagfunc with diagonal components of the PairGrid, which do not have name property, let alone name anywhere else in the arguments passed. Here is what is passed to the first time diagfunc is called:

{
#  this is what you passed as positional parameter
#  As you'll notice x is just an array, and
#   does not have any additional properties like a label
'x': array([5.1, 4.9, , ... 5.9]),
#  these get passed in as **kws
'label': '_nolegend_',
'color': (0.12156862745098039, 0.4666666666666667, 0.7058823529411765)
}


Now, if we could somehow iterate over all labels in IRIS dataset, we could put each one against the diagnoals of the PairGrid. Since diagfunc is called each time and has a new scope, I found that having an iterator that returned a new label each time it was called solves that.

next_iris_label defined as

next_iris_label = iter(iris).__next__


solves that problem. This only has one constraint that I dont like, you will need to reset this by creating a new instance of the iterator by the binding statement next_iris_label = iter(iris).__next__ when you want to do such labelling again(otherwise it would throw a StopIteration error.)