Postgresql 面对x轴和y轴(标签和范围)bokeh图的多个问题

Postgresql 面对x轴和y轴(标签和范围)bokeh图的多个问题,postgresql,python-3.x,plot,bokeh,Postgresql,Python 3.x,Plot,Bokeh,我试图实现类似于bokeh图的示例,从postgresql数据库获取数据。我几乎完成了所有的事情,除了一件事,比如我的情节没有被渲染 在我的场景中,当我尝试渲染绘图时,它成功地从数据库中获取记录,并在日志中显示数据,但它不会渲染我的绘图。找到下面的屏幕截图: 另外,如果您注意到,它会显示已选择2个事件,并且它也不会渲染x轴和y轴标签及其范围 这是我的代码: from os.path import dirname, join import sys import numpy as np impor

我试图实现类似于bokeh图的示例,从postgresql数据库获取数据。我几乎完成了所有的事情,除了一件事,比如我的情节没有被渲染

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

另外,如果您注意到,它会显示已选择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()

从昨天开始,我一直试图自己调试这个问题,但未能成功,所以请帮助我。谢谢。

关键问题是,对于分类数据,需要明确提供x_范围和y_范围作为分类因子。您的案例稍微复杂一些,因为您需要动态设置分类因子,此外,您还可以将日期作为可能的x/y轴

如果根本不设置x_范围、y_范围,则不会显示任何内容 比如说,, 如果在地物实例化上设置y_范围、x_范围参数, 它将自动配置正确的x/y标签。 如果你设定 y_范围、x_范围属性在实例化之后,您还需要 构建x轴和y轴记号标记。 我已经输入了一组样本数据,但由于输入的日期,我破坏了筛选。请注意,分类轴中不能有冒号,因此您必须提出一个解决方案,在选择日期时可能会创建日期时间轴,或者该轴的格式与数据库值不同

from os.path import dirname, join
import sys
import time
import numpy as np
import pandas.io.sql as psql
import pandas as pd
import psycopg2
from datetime import datetime
from bokeh.plotting import figure
from bokeh.layouts import layout, widgetbox
from bokeh.models import ColumnDataSource, HoverTool, Div, FactorRange, FuncTickFormatter, FixedTicker
from bokeh.models.widgets import Slider, Select, TextInput, DatePicker, DateRangeSlider
from bokeh.io import curdoc
gender=["Male","Male","Female","Female"]
religion=['r1','r2','r1','r3']
state = ['s1','s2','s1','s3']
date=['d1','d2','d3','d4']
caste = ['c1','c2','c3','c4']
title=['t1','t2','t3','t4']
data = {'gender':gender,'religion':religion,'state':state,'date_published':date,'caste':caste,'title':title}
movies = pd.DataFrame(data)

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="blah blah", 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=['r1','r2','r2'])
state = Select(title="State", value="All",
               options=['s1','s2','s3'])
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]
    # ignore filtering for example plotting.
    selected = movies
    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"]
    )
    xticker = FixedTicker(ticks=list(range(1,len(df[x_name])+1)))
    yticker = FixedTicker(ticks=list(range(1,len(df[y_name])+1)))
    # func tick formatter maps each tick defined by the x/y ticker, 
    # then maps this to the returned value which is displayed on the 
    #  plot
    yformatter = FuncTickFormatter(args={'source':source},code="""                        
          return source.data['y'][tick-1];
          """)
    xformatter = FuncTickFormatter(args={'source':source},code="""
          return source.data['x'][tick-1];
          """)
    p.x_range = FactorRange(factors=list(df[x_name].values),bounds='auto')
    p.y_range = FactorRange(factors=list(df[y_name].values),bounds='auto')
    p.renderers[0].ticker = xticker
    p.renderers[2].ticker = yticker
    p.renderers[0].formatter = xformatter
    p.renderers[2].formatter = yformatter

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"

关键问题是,对于分类数据,需要显式地提供x_范围和y_范围作为分类因子。您的案例稍微复杂一些,因为您需要动态设置分类因子,此外,您还可以将日期作为可能的x/y轴

如果根本不设置x_范围、y_范围,则不会显示任何内容 比如说,, 如果在地物实例化上设置y_范围、x_范围参数, 它将自动配置正确的x/y标签。 如果你设定 y_范围、x_范围属性在实例化之后,您还需要 构建x轴和y轴记号标记。 我已经输入了一组样本数据,但由于输入的日期,我破坏了筛选。请注意,分类轴中不能有冒号,因此您必须提出一个解决方案,在选择日期时可能会创建日期时间轴,或者该轴的格式与数据库值不同

from os.path import dirname, join
import sys
import time
import numpy as np
import pandas.io.sql as psql
import pandas as pd
import psycopg2
from datetime import datetime
from bokeh.plotting import figure
from bokeh.layouts import layout, widgetbox
from bokeh.models import ColumnDataSource, HoverTool, Div, FactorRange, FuncTickFormatter, FixedTicker
from bokeh.models.widgets import Slider, Select, TextInput, DatePicker, DateRangeSlider
from bokeh.io import curdoc
gender=["Male","Male","Female","Female"]
religion=['r1','r2','r1','r3']
state = ['s1','s2','s1','s3']
date=['d1','d2','d3','d4']
caste = ['c1','c2','c3','c4']
title=['t1','t2','t3','t4']
data = {'gender':gender,'religion':religion,'state':state,'date_published':date,'caste':caste,'title':title}
movies = pd.DataFrame(data)

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="blah blah", 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=['r1','r2','r2'])
state = Select(title="State", value="All",
               options=['s1','s2','s3'])
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]
    # ignore filtering for example plotting.
    selected = movies
    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"]
    )
    xticker = FixedTicker(ticks=list(range(1,len(df[x_name])+1)))
    yticker = FixedTicker(ticks=list(range(1,len(df[y_name])+1)))
    # func tick formatter maps each tick defined by the x/y ticker, 
    # then maps this to the returned value which is displayed on the 
    #  plot
    yformatter = FuncTickFormatter(args={'source':source},code="""                        
          return source.data['y'][tick-1];
          """)
    xformatter = FuncTickFormatter(args={'source':source},code="""
          return source.data['x'][tick-1];
          """)
    p.x_range = FactorRange(factors=list(df[x_name].values),bounds='auto')
    p.y_range = FactorRange(factors=list(df[y_name].values),bounds='auto')
    p.renderers[0].ticker = xticker
    p.renderers[2].ticker = yticker
    p.renderers[0].formatter = xformatter
    p.renderers[2].formatter = yformatter

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"

实际上,在类别标签中有问题的是冒号,而不是分号,但请注意,在Bokeh中有一个重构类别的开放PR。当0.12.7发布时,任何字符串都将对类别有效。实际上,在类别标签中存在问题的是冒号,而不是分号,但请注意,在Bokeh中有一个重构类别的开放PR。当0.12.7发布时,任何字符串都将对分类有效。