关于x轴和y轴(标签和范围)的散景图面临多个问题

时间:2017-07-23 06:25:08

标签: postgresql python-3.x plot bokeh

我正在尝试实现像散景图movies这样的示例,从postgresql数据库中获取数据。我几乎已经实现了一切,除了一件事情,比如我的情节没有得到渲染。

在我的场景中,当我尝试渲染绘图时,它会从数据库中成功获取记录,而在日志中它会显示数据,但它不会渲染我的绘图。找到以下截图:

Not rendering plot

另外,如果你注意到,它表示选择了2个事件,它也没有渲染x轴和& y轴标签及其范围。

这是我的代码:

from os.path import dirname, join
import sys
import numpy as np
import pandas.io.sql as psql

import psycopg2
from datetime import datetime
from bokeh.plotting import figure
from bokeh.layouts import layout, widgetbox
from bokeh.models import ColumnDataSource, HoverTool, Div
from bokeh.models.widgets import Slider, Select, TextInput, DatePicker, DateRangeSlider
from bokeh.io import curdoc

try:

    #Create a database session
    conn = psycopg2.connect(database='movies', user='postgres', password='postgres')

    #Create a client cursor to execute commands
    cursor = conn.cursor()

    #The variables placeholder must always be a %s, psycop2 will automatically convert the values to SQL literal
    query = "SELECT * FROM atrocities_details"
    movies = psql.read_sql(query, conn)

    movies["color"] = np.where(movies["gender"] == 'Male', "orange", "grey")
    movies["alpha"] = np.where(movies["gender"] == 'Male', 0.9, 0.25)
    movies.fillna(0, inplace=True)  # just replace missing values with zero

    axis_map = {
        "Date": "date_published",
        "State": "state",
        "Gender": "gender",
        "Religion": "religion",
        "Caste": "caste"
    }

    desc = Div(text=open(join(dirname(__file__), "description.html")).read(), width=800)

    # Create Input controls
    min_date = Slider(title="Date From", start=2000, end=2017, value=2010, step=1)
    max_date = Slider(title="Date To", start=2001, end=2018, value=2018, step=1)
    religion = Select(title="Religion", value="All",
                   options=open(join(dirname(__file__), 'religion.txt')).read().split())
    state = Select(title="State", value="All",
                   options=open(join(dirname(__file__), 'states.txt')).read().split())
    incident_name = TextInput(title="Incident name contains")
    x_axis = Select(title="X Axis", options=sorted(axis_map.keys()), value="Caste")
    y_axis = Select(title="Y Axis", options=sorted(axis_map.keys()), value="Date")

    # Create Column Data Source that will be used by the plot
    source = ColumnDataSource(data=dict(x=[], y=[], color=[], title=[], alpha=[]))

    hover = HoverTool(tooltips=[
        ("Title", "@title")
    ])

    p = figure(plot_height=500, plot_width=500, title="", toolbar_location=None, tools=[hover])
    p.circle(x="x", y="y", source=source, size=7, color="color", line_color=None, fill_alpha="alpha")

    def select_movies():
        religion_val = religion.value
        state_val = state.value
        incident_name_val = incident_name.value.strip()

        # Compare form details with the database object that we created above and return the result
        selected = movies[
            (movies.date_published >= str(min_date.value) + "-01-01 00:00:00") &
            (movies.date_published <= str(max_date.value) + "-01-01 00:00:00")
        ]

        if (religion_val != "All"):
            selected = selected[selected.religion.str.contains(religion_val)==True]
        if (state_val != "All"):
            selected = selected[selected.state.str.contains(state_val)==True]
        if (incident_name_val != ""):
            selected = selected[selected.title.str.contains(incident_name_val)==True]
        return selected

    def update():
        df = select_movies()
        x_name = axis_map[x_axis.value]
        y_name = axis_map[y_axis.value]
        p.xaxis.axis_label = x_axis.value
        p.yaxis.axis_label = y_axis.value
        p.title.text = "%d incident selected" % len(df)
        source.data = dict(
            x=df[x_name],
            y=df[y_name],
            color=df["color"],
            title=df["title"],
            alpha=df["alpha"]
        )

    controls = [religion, min_date, max_date, state, incident_name, x_axis, y_axis]
    for control in controls:
        control.on_change('value', lambda attr, old, new: update())

    sizing_mode = 'fixed'  # 'scale_width' also looks nice with this example

    inputs = widgetbox(*controls, sizing_mode=sizing_mode)
    l = layout([
        [desc],
        [inputs, p],
    ], sizing_mode=sizing_mode)

    update()  # initial load of the data

    curdoc().add_root(l)
    curdoc().title = "Movies"

    #print(cursor.fetchone())
except psycopg2.DatabaseError as e:
    print ('Error %s' % e)
    sys.exit(1)
finally:
    if conn:
        conn.close()

我试图从昨天起自己调试这个问题,但无法取得成功,所以请帮助我。感谢。

1 个答案:

答案 0 :(得分:2)

关键问题是,对于分类数据,您需要明确提供x_range和y_range作为分类因素。您的情况稍微复杂一些,因为您需要动态设置分类因子,另外还有日期作为可能的x / y轴。

  • 如果您没有设置x_range,y_range,则不会显示任何内容 (例如,参见Nominal/categorical axis on a scatter plot
  • 如果在图形实例化中设置y_range,x_range参数, 它会自动配置正确的x / y标签。
  • 如果你设定 实例化后的y_range,x_range attrs,你还需要 构造x和y轴刻度线。

我已经输入了一组样本数据,但由于我输入的日期,我打破了过滤。请注意,你不能在分类轴上放置冒号,所以你必须在那里找到一个解决方案(也许选择日期时创建日期时间轴或轴的不同格式与数据库值比较

from itertools import groupby

data = "1 50 30 1000   20 4 1 2"
numbers = []

for k, g in groupby(data, lambda c: c.isdigit()):
    if k:
        numbers.append(int(''.join(g)))

print numbers