1

I believe a similar question to this was asked before, but it didn't really clarify things for me.

Basically, I have a list of tuples, each of which functions as a point (so (x, y, z) for example).

I want to plot them in either 2D or 3D after I run a clustering algorithm (that color codes the points).

clusters = []
def plotPoints(self):
    fig = plt.figure()
    if self.clusters[0].dimensions == 2:
        ax = fig.add_subplot(111)
        for i in range(0, len(self.clusters)):
            ax.scatter(*zip(*self.clusters[i].points), c=self.clusters[i].color, marker="o", label=("Cluster " + str(i + 1)))
        plt.legend(loc='upper left')
        plt.show()
    elif self.clusters[0].dimensions == 3:
        ax = fig.add_subplot(111, projection='3d')
        for i in range(0, len(self.clusters)):
            ax.scatter(*zip(*self.clusters[i].points), c=self.clusters[i].color, marker="o", label=("Cluster " + str(i + 1)))
        plt.legend(loc='upper left')
        plt.show()
    else:
        print "Cannot plot in dimensions lower than 2 or higher than 3"

Cluster class:

class Cluster(object):
centroid = ()
dimensions = 0
color = 'k'

def __init__(self, init_pt, color):
    self.points = []
    self.points.append(init_pt)
    self.dimensions = len(init_pt)
    self.centroid = init_pt
    self.color = color

def addPoint(self, pt):
    try:
        if len(pt) != self.dimensions:
            raise ArithmeticError("Wrong number of dimensions on new point, ignoring")
        else:
            centroid_dim_list = []
            for dim in range(0, self.dimensions):
                centroid_dim_list.append((self.centroid[dim] * len(self.points) + pt[dim]) / float(len(self.points) + 1))
            self.centroid = tuple(centroid_dim_list)
            self.points.append(pt)
    except ArithmeticError as ae:
        print ae.message
        pass

2D plots work just fine (and look really nice), but 3D plots give me a warning:

UserWarning: No labeled objects found. Use label='...' kwarg on individual plots.
  warnings.warn("No labeled objects found. "

And no legend appears. But I am labeling the points, and the code I am using is almost identical, so I am confused as to what the problem is. I heard something about a proxy object, but I have no clue how to use that for this case.

Chris Chambers
  • 1,367
  • 21
  • 39

3 Answers3

5

I partially solved my problem by adding the following code:

ax.plot([], [], 'o', c=self.clusters[i].color, label="Cluster " + str(i + 1))

to each iteration of the loop, giving the following figure.

enter image description here

The marker is doubled for some reason, but at least it mostly works. If someone can comment on why it is doubled that would be great.

Edit Per jedwards comment, I fixed it by changing my legend() call to:

plt.legend(numpoints=1 , loc='upper left')
Chris Chambers
  • 1,367
  • 21
  • 39
  • 1
    re: double markers, see [this question](http://stackoverflow.com/questions/6146778/matplotlib-legend-markers-only-once) – jedwards Mar 08 '15 at 23:16
2

I'm not sure what the format of your self.clusters is, but with something I came up with that I think closely resembles it, I was able to produce the following:

1

With the code

for i,c in enumerate(self.clusters):
    x,y,z = c.points
    ax.text(x,y,z, "Cluster %d" % (i+1), None)

Based off text3d demo.

I'm using matplotlib 1.4.3

Note, you won't be able to "drag and drop" this code, since my cluster instances are simple 3-tuples, yours seem more complicated.

jedwards
  • 29,432
  • 3
  • 65
  • 92
  • My clusters are lists of 3-tuples, and I have a list of them (the clusters), so it probably would work. I don't want the text labels to show. I'll include more code in the question. – Chris Chambers Mar 08 '15 at 22:37
  • Tried that, shows the label on the graph, but I just want the legend to work, and it still shows the same warning. – Chris Chambers Mar 08 '15 at 22:44
1

I may not fully understood your question, but here is an example you may tweak it to your case. Hope it helps:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from random import randint

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

# dataset
def data():
    return [randint(0,100) for _ in range(10)]

c1 = (data(), data(), data())
c2 = (data(), data(), data())
c3 = (data(), data(), data())
clusters = [c1, c2, c3]

# plot 
colors = ['r', 'b', 'y', 'c']
for i, c in enumerate(clusters):
    ax.scatter(c[0], c[1], c[2], c=colors[i], label='cluster {}'.format(i))

ax.legend(bbox_to_anchor = (1.5, 1))
plt.show()

It produces this: enter image description here

Aziz Alto
  • 19,057
  • 5
  • 77
  • 60