Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to plot with different colors when using Dot Charts and Bar Charts? #1967

Closed
pofican opened this issue Feb 24, 2015 · 16 comments
Closed

Comments

@pofican
Copy link

pofican commented Feb 24, 2015

I would like to have a dot/bar plot where each dot/bar that belongs to a different category has a different color, is it possible to do this with Bokeh?

Also when I try to plot a dot chart lets say for 3 diffenrent group of categories all the dot plots appears to be red. Is there a way I could at least change the color of dot plots for different data sets?

Thanks in advance!

@birdsarah
Copy link
Member

It might be easier to help with some specific code that's not working. I believe there are examples in the gallery and in the examples folder that cover this need:

http://bokeh.pydata.org/en/latest/docs/gallery/iris.html
http://bokeh.pydata.org/en/latest/docs/gallery/stacked_bar_chart.html

Also see my comment here, about how to customize those colors.

@pofican
Copy link
Author

pofican commented Feb 24, 2015

OK the above link for your comment worked I am able to plot different Dot/Bar charts in different colors.
One of my question above was regarding chaning the dot colors within the charts, is it possible to plot a Dot/Bar chart where each dot/bar in that chart has a different color?
So while coding I realized I can not specify a color for each data point, that is why I don't have a sample code for this at the moment.
Here couple of examples about what mean: http://www.advsofteng.com/gallery_bar2.html

@birdsarah
Copy link
Member

@fpliger
Copy link
Contributor

fpliger commented Feb 25, 2015

Sorry @pofican I'd love to be more helpful but I don't understand your needs. I'm not sure if you need a new a mixed Scatter and Bar chart or just a different use the Dot and Bar.

@pofican
Copy link
Author

pofican commented Feb 25, 2015

@birdsarah @fpliger scatter example is not helpful for my case, below I put a sample code which might help you understand what I am trying to do and this code is not working because the expected data type for a Dot object is a list not an element of a list. Briefly, lets say I have a data list and I want to assign a different color for each element of this list and plot it using Dot/Bar, like in the example below I would like to see x(it's value in this case is 1) green, y (value 2) blue, and z (value 3) yellow. I didn't see any related examples in the documentation, am I missing anything here? Is this doable with Bokeh?

First scenario (The example is trying different colors of blue. I don't even know how I can assign green, blue, yellow to each data point):

x = [1, 2, 3]
y = ['x', 'y', 'z']
output_file("dots.html")
palette_key = 3
for i,j in zip(x,y):
    palette = brewer["Blues"][palette_key]
    dots = Dot(i, cat = j, title="Data", ylabel='FP Rate', xlabel='Vendors',filename="dots.html", legend=False, palette = palette)
    palette_key = palette_key + 1
show(VBox(HBox(dots)))
>>> ValueError: expected an element of either Bool or List(Any), got 'x'

Second scenario, I tried cat= None and getting another error related to the first element of Dot function, because it expects a list not an element of the list.

x = [1, 2, 3]
y = ['x', 'y', 'z']
output_file("dots.html")
palette_key = 3
for i,j in zip(x,y):
    palette = brewer["Blues"][palette_key]
    dots = Dot(i, cat = None, title="Data", ylabel='FP Rate', xlabel='Vendors',filename="dots.html", legend=False, palette = palette)
    palette_key = palette_key + 1
show(VBox(HBox(dots)))

>>>> Input type not supported! 1

@pofican
Copy link
Author

pofican commented Feb 27, 2015

@fpliger @birdsarah @damianavila @bryevdv any ideas? Is my question clear? Is this doable with bokeh?

@bryevdv
Copy link
Member

bryevdv commented Feb 28, 2015

@pofican palettes operate on grouped data in a single plot. I still can't tell if you want all the data on one plot or not. But in any case, you are not passing in groups. Try this to get dots with different colors on one plot:

from bokeh.charts import *
xyvalues=dict(x=[1], y=[2], z=[3])
dots = Dot(
    xyvalues, title="Data", ylabel='FP Rate', 
    xlabel='Vendors',filename="dots.html", legend=False, 
    palette=["red", "green", "blue"])
show(dots)

If you want multiple charts with a different color for each chart, I am actually not sure there is a way to control the "dot" color directly (if not that is an oversight). @fpliger can you comment, is there a way to control the dot color? I tried passing a color argument but that did not work.

@bryevdv
Copy link
Member

bryevdv commented Feb 28, 2015

Here is a better example:

cat = ['foo', 'bar', 'baz']
xyvalues=dict(x=[1,4,5], y=[2,7,3], z=[3,4,5])
dots = Dot(
    xyvalues, cat=cat, title="Data", 
    ylabel='FP Rate', xlabel='Vendors',
    legend=False, palette=["red", "green", "blue"])
show(dots)

screen shot 2015-02-28 at 2 10 11 pm

@pofican
Copy link
Author

pofican commented Mar 2, 2015

@bryevdv the first example that you put above what I was asking for but when I add categories all values are plotted under just one category. I couldn't find a way to get around this... Any ideas?

from bokeh.charts import *
xyvalues=dict(x=[1], y=[2], z=[3])
cat = xyvalues.keys()
dots = Dot(
    xyvalues, cat = cat, title="Data", ylabel='FP Rate', 
    xlabel='Vendors',filename="dots.html", legend=False, 
    palette=["red", "green", "blue"])
show(dots)

index

@fpliger
Copy link
Contributor

fpliger commented Mar 2, 2015

@pofican In your last example you are listing 3 categories but on data series you are only providing one value (so only for the first category).

The categories refers to the categories to map to the values listed on each data series element and not to the data series themselves. Is it should have the same length as the values lists of your data (xyvalues) and not the length of the data itself. The documentation still needs a lot of improvements. We'll also take care of making it more clear. I've opened #2005 to address this issue and add more improvements to the docs.

@pofican
Copy link
Author

pofican commented Mar 2, 2015

@fpliger I didn't clearly get what you are saying above. What is the proper way of calling Dots function with a list of values where each value maps a different category? If you can give a simple example that would be great. Because I don't think what I am asking is doable using Bokeh, I have tried couple of ways and none of them worked.

@fpliger
Copy link
Contributor

fpliger commented Mar 3, 2015

@pofican sorry for not being clear... I'll try to comment your example above (with some modifications to make it easier) to explain:

from bokeh.charts import *
# here you create the data to be consumed by the Dot chart. It is a dict where:
# * the keys define the names of the series to display . Basically defines how many 
# * the values are iterables of scalar where each scalar value defines the value of the serie to show 
#   on each serie. Basically defines the height of the dot on the specific category.
xyvalues=dict(A=[1, 2], B=[2, 2], C=[3, 7]) # on your example was 'xyvalues=dict(x=[1], y=[2], z=[3])'

# the categories related to the values defined in xyvalues. This should have the same length
# of the iterables defined as values in xyvalues
cat = ["BOB", "BEN"] #on your example was 'cat = xyvalues.keys()'

dots = Dot(
    xyvalues, cat = cat, title="Data", ylabel='FP Rate', 
    xlabel='Vendors',filename="dots.html", legend=False, 
    palette=["red", "green", "blue"])
show(dots)

The previous code creates:

screen shot 2015-03-03 at 12 35 23

Let me know if it's clears your doubts. Thanks!

@pofican
Copy link
Author

pofican commented Mar 3, 2015

@fpliger @bryevdv
Can you provide an example where each category has a single data point, but each point has a different color? This is what I would like to do. Thank you.

@pofican
Copy link
Author

pofican commented Mar 3, 2015

@fpliger @bryevdv

I modifed the circle example and able to plot the below. But I would like to be plot a similar plot using Dot/Bar, is this possible?

from bokeh.plotting import *



factors = ["a", "b", "c"]
x0 = [0, 0, 0]
x =  [50, 40, 65]

output_file("categorical.html", title="categorical.py example")

p1 = figure(title="Dot Plot", tools="resize,save", y_range=factors, x_range=[0,100])

p1.segment(x0, factors, x, factors, line_width=2, line_color=["orange","green","red"])
p1.circle(x, factors, size=15, fill_color=["orange","green","red"], line_color=["orange","green","red"], line_width=3, )
show(p1)

dot

@fpliger
Copy link
Contributor

fpliger commented Mar 4, 2015

@pofican you are considering categories conceptually in a different way they are in charts. AFAIK charts (both Bar and Dot) cannot produce what you'd like. But the good new is that it's fairly easy to create a new chart builders that does what you'd like. Here's a quick implementation that should do what you need:

import numpy as np
from bokeh.charts import *
from bokeh.plotting import show, output_file
from bokeh.charts._builder import create_and_build
from bokeh.charts.builder.dot_builder import DotBuilder, cycle_colors, FactorRange, \
    Range1d, make_scatter, Segment, GlyphRenderer

class FlatDotBuilder(DotBuilder):
    def _process_data(self):
        if not self.cat:
            self.cat = [str(x) for x in self._values.index]

        values = self._values.values()
        self._data = dict(
            cat=self.cat, zero=np.zeros(len(self.cat)),
            values=values[0],
            colors=cycle_colors(values[0], self.palette)
        )
        self._groups=self._values.keys()

    def _set_sources(self):
        self._source = ColumnDataSource(self._data)
        self.x_range = FactorRange(factors=self._source.data["cat"])
        end = 1.1 * max(self._data['values'])
        self.y_range = Range1d(start=0, end=end)

    def _yield_renderers(self):
        if self.stem:
            glyph = Segment(
                x0='cat', y0='zero', x1='cat', y1='values',
                line_color="colors", line_width=2
            )
            yield GlyphRenderer(data_source=self._source, glyph=glyph)

        renderer = make_scatter(
            self._source, 'cat', 'values', 'circle',
            'colors', size=15, fill_alpha=1.
        )
        self._legends.append((self._groups[0], [renderer]))
        yield renderer

def FlatDot(values, cat=None, stem=True, xscale="categorical", yscale="linear",
        xgrid=False, ygrid=True, **kws):
    return create_and_build(
        FlatDotBuilder, values, cat=cat, stem=stem, xscale=xscale, yscale=yscale,
        xgrid=xgrid, ygrid=ygrid, **kws
    )

cat = ["BOB", "BEN", "JOB"]
output_file('dots.html')
# this works with just [10, 20, 30] as well
dots = FlatDot(
    {"FP RATE": [10, 20, 30]},
    cat=cat, title="Data", ylabel='FP Rate',
    xlabel='Vendors',
    palette=["red", "yellow", "blue"]
)
show(dots)

please note that legend "is wrong" but I left as is because I'm not really sure how you'd like it to behave. Here's how the former code looks like:

screen shot 2015-03-04 at 11 45 12

@pofican
Copy link
Author

pofican commented Mar 4, 2015

@fpliger Thanks for the response, I am closing this discussion at this point then.

@pofican pofican closed this as completed Mar 4, 2015
@damianavila damianavila added this to the 0.8.2 milestone Mar 17, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants