def encode_color(self): AltairChart.encode_color(self) self.add_text() # Setting tooltip as non-null self.chart = self.chart.configure_mark( tooltip=alt.TooltipContent("encoding")) self.code += f"""chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding'))"""
def initialize_chart(self): x_attr = self.view.get_attr_by_channel("x")[0] y_attr = self.view.get_attr_by_channel("y")[0] x_min = self.view.x_min_max[x_attr.attribute][0] x_max = self.view.x_min_max[x_attr.attribute][1] y_min = self.view.y_min_max[y_attr.attribute][0] y_max = self.view.y_min_max[y_attr.attribute][1] chart = alt.Chart(self.data).mark_circle().encode( x=alt.X(x_attr.attribute, scale=alt.Scale(domain=(x_min, x_max)), type=x_attr.data_type), y=alt.Y(y_attr.attribute, scale=alt.Scale(domain=(y_min, y_max)), type=y_attr.data_type)) chart = chart.configure_mark(tooltip=alt.TooltipContent( 'encoding')) # Setting tooltip as non-null chart = chart.interactive() # Enable Zooming and Panning ##################################### ## Constructing Altair Code String ## ##################################### self.code += "import altair as alt\n" dfname = "df" # TODO: Placeholder (need to read dynamically via locals()) self.code += f''' chart = alt.Chart({dfname}).mark_circle().encode( x=alt.X('{x_attr.attribute}',scale=alt.Scale(domain=({x_min}, {x_max})),type='{x_attr.data_type}'), y=alt.Y('{y_attr.attribute}',scale=alt.Scale(domain=({y_min}, {y_max})),type='{y_attr.data_type}') ) chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null chart = chart.interactive() # Enable Zooming and Panning ''' return chart
def initialize_chart(self): # return NotImplemented x_attr = self.vis.get_attr_by_channel("x")[0] y_attr = self.vis.get_attr_by_channel("y")[0] chart = alt.Chart(self.data).mark_rect().encode( x=alt.X('xBinStart', type='quantitative', axis=alt.Axis(title=x_attr.attribute), bin = alt.BinParams(binned=True)), x2=alt.X2('xBinEnd'), y=alt.Y('yBinStart', type='quantitative', axis=alt.Axis(title=y_attr.attribute), bin = alt.BinParams(binned=True)), y2=alt.Y2('yBinEnd'), opacity = alt.Opacity('count',type='quantitative',scale=alt.Scale(type="log"),legend=None) ) chart = chart.configure_scale(minOpacity=0.1,maxOpacity=1) chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null chart = chart.interactive() # Enable Zooming and Panning #################################### # Constructing Altair Code String ## #################################### self.code += "import altair as alt\n" # self.code += f"visData = pd.DataFrame({str(self.data.to_dict(orient='records'))})\n" self.code += f"visData = pd.DataFrame({str(self.data.to_dict())})\n" self.code += f''' chart = alt.Chart(visData).mark_rect().encode( x=alt.X('xBinStart', type='quantitative', axis=alt.Axis(title='{x_attr.attribute}'), bin = alt.BinParams(binned=True)), x2=alt.X2('xBinEnd'), y=alt.Y('yBinStart', type='quantitative', axis=alt.Axis(title='{y_attr.attribute}'), bin = alt.BinParams(binned=True)), y2=alt.Y2('yBinEnd'), opacity = alt.Opacity('count',type='quantitative',scale=alt.Scale(type="log"),legend=None) ) chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null ''' return chart
def encode_color( self ): # override encode_color in AltairChart to enforce add_text occurs afterwards AltairChart.encode_color(self) self.add_text() self.chart = self.chart.configure_mark(tooltip=alt.TooltipContent( 'encoding')) # Setting tooltip as non-null self.code += f'''chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding'))'''
def initialize_chart(self): x_attr = self.vis.get_attr_by_channel("x")[0] y_attr = self.vis.get_attr_by_channel("y")[0] x_attr_abv = x_attr.attribute y_attr_abv = y_attr.attribute if len(x_attr.attribute) > 25: x_attr_abv = x_attr.attribute[:15] + "..." + x_attr.attribute[-10:] if len(y_attr.attribute) > 25: y_attr_abv = y_attr.attribute[:15] + "..." + y_attr.attribute[-10:] x_min = self.vis.min_max[x_attr.attribute][0] x_max = self.vis.min_max[x_attr.attribute][1] y_min = self.vis.min_max[y_attr.attribute][0] y_max = self.vis.min_max[y_attr.attribute][1] x_attr.attribute = x_attr.attribute.replace(".", "") y_attr.attribute = y_attr.attribute.replace(".", "") chart = ( alt.Chart(self.data) .mark_circle() .encode( x=alt.X( x_attr.attribute, scale=alt.Scale(domain=(x_min, x_max)), type=x_attr.data_type, axis=alt.Axis(title=x_attr_abv), ), y=alt.Y( y_attr.attribute, scale=alt.Scale(domain=(y_min, y_max)), type=y_attr.data_type, axis=alt.Axis(title=y_attr_abv), ), ) ) # Setting tooltip as non-null chart = chart.configure_mark(tooltip=alt.TooltipContent("encoding")) chart = chart.interactive() # Enable Zooming and Panning ##################################### ## Constructing Altair Code String ## ##################################### self.code += "import altair as alt\n" dfname = "placeholder_variable" self.code += f""" chart = alt.Chart({dfname}).mark_circle().encode( x=alt.X('{x_attr.attribute}',scale=alt.Scale(domain=({x_min}, {x_max})),type='{x_attr.data_type}', axis=alt.Axis(title='{x_attr_abv}')), y=alt.Y('{y_attr.attribute}',scale=alt.Scale(domain=({y_min}, {y_max})),type='{y_attr.data_type}', axis=alt.Axis(title='{y_attr_abv}')) ) chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null chart = chart.interactive() # Enable Zooming and Panning """ return chart
def initialize_chart(self): self.tooltip = False x_attr = self.view.get_attr_by_channel("x")[0] y_attr = self.view.get_attr_by_channel("y")[0] self.code += "import altair as alt\n" # self.code += f"visData = pd.DataFrame({str(self.data.to_dict(orient='records'))})\n" self.code += f"visData = pd.DataFrame({str(self.data.to_dict())})\n" if (x_attr.data_model == "measure"): agg_title = get_agg_title(x_attr) y_attr_field = alt.Y(y_attr.attribute, type=y_attr.data_type, axis=alt.Axis(labelOverlap=True)) x_attr_field = alt.X(x_attr.attribute, type=x_attr.data_type, title=agg_title) y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', axis=alt.Axis(labelOverlap=True))" x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', title='{agg_title}')" if (y_attr.sort == "ascending"): y_attr_field.sort = "-x" y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', axis=alt.Axis(labelOverlap=True), sort ='-x')" else: agg_title = get_agg_title(y_attr) x_attr_field = alt.X(x_attr.attribute, type=x_attr.data_type, axis=alt.Axis(labelOverlap=True)) y_attr_field = alt.Y(y_attr.attribute, type=y_attr.data_type, title=agg_title) x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', axis=alt.Axis(labelOverlap=True))" y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', title='{agg_title}')" if (x_attr.sort == "ascending"): x_attr_field.sort = "-y" x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', axis=alt.Axis(labelOverlap=True),sort='-y')" chart = alt.Chart(self.data).mark_bar().encode(y=y_attr_field, x=x_attr_field) # TODO: tooltip messes up the count() bar charts # Can not do interactive whenever you have default count measure otherwise output strange error (Javascript Error: Cannot read property 'length' of undefined) #chart = chart.interactive() # If you want to enable Zooming and Panning chart = chart.configure_mark(tooltip=alt.TooltipContent( 'encoding')) # Setting tooltip as non-null self.code += f''' chart = alt.Chart(visData).mark_bar().encode( y = {y_attr_field_code}, x = {x_attr_field_code}, ) chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null ''' return chart
def plotAltairLineChart(self): """ Function to output Altair line chart Returns ------- Altair line chart """ # Date that will be used to indicate "Data as of user chosen date" iso_date = self.covid19_date.strftime('%Y-%m-%d') # Altair charting API only charts dataframe columns, not index, so need to apply reset_index() alt_chart = alt.Chart(self.dfByState()[:iso_date] .query("State_Province in(@self.state_province)") .reset_index() .rename(columns={'index': 'Date'}) ).mark_line().encode( x=alt.X(title='Date', field='Date', type='temporal'), y=alt.Y(title='# of ' + self.confirmed_deaths, field='Qty_Confirmed', type='quantitative', scale=alt.Scale(type=self.ylog) ), color=alt.Color(field='State_Province', type='nominal', legend=alt.Legend(title="State/Province") ), tooltip=[alt.Tooltip(field='State_Province', type= 'nominal'), alt.Tooltip(field='Qty_Confirmed', type= 'quantitative'), alt.Tooltip(field='Date', type= 'temporal') ] ) # To create filled circles in the legend per # https://github.com/altair-viz/altair/issues/1206 points = alt.Chart(self.dfByState()[:iso_date] .query("State_Province in(@self.state_province)") .reset_index() .rename(columns={'index': 'Date'}) ).mark_circle(size=0).encode( color='State_Province' ) # To add hover tips, but make it less sensitive per # https://github.com/altair-viz/altair/issues/1812 tooltips = alt_chart.mark_point(size=100, opacity=0, tooltip=alt.TooltipContent("data") ) alt_chart = alt_chart + points + tooltips return alt_chart.properties( title='COVID-19 ' + self.confirmed_deaths, width='container', height=400 )
def initializeChart(self): # UP TO HERE: Broken because self.expandUnderspecified() in dataObj does not run when there are multiple object, we should not rely on spec # measures = list(filter(lambda x: x.dataModel=="measure" if hasattr(x,"dataModel") else False,self.dobj.spec)) xAttr = self.dobj.getObjFromChannel("x")[0].columnName yAttr = self.dobj.getObjFromChannel("y")[0].columnName chart = alt.Chart(self.dataURL).mark_circle().encode( x=alt.X(xAttr, scale=alt.Scale(zero=False)), y=alt.Y(yAttr, scale=alt.Scale(zero=False))) chart = chart.configure_mark(tooltip=alt.TooltipContent( 'encoding')) # Setting tooltip as non-null chart = chart.interactive( ) # If you want to enable Zooming and Panning return chart
def covid19TimeSeriesByCountry(covid19_date: Date, confirmed_deaths: str, country: List[str] = ['US'], ylog: bool = False) -> PanelRow: """Function that returns a Panel dashboard displaying confirmed COVID-19 cases It is using Panel's "Reactive functions" API: https://panel.holoviz.org/user_guide/APIs.html Parameters ---------- covid19_date : Date End date of data you wish to obtain up to country : List[str] One or more countries for which you would like to obtain data for (default='US') confirmed_deaths : str Option to choose # of confirmed cases or # of deaths from Covid-19 ylog: bool Whether or not to apply log scaling to y-axis. Default is False Returns ------- Panel object """ iso_date: str = covid19_date.strftime('%Y-%m-%d') # Source of COVID-19 data if confirmed_deaths == 'Confirmed Cases': url: str = 'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv' else: url: str = 'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_deaths_global.csv' df: DataFrame = pd.read_csv(url) df_countries: DataFrame = (df.drop( columns=['Province/State', 'Lat', 'Long']).groupby( 'Country/Region').agg('sum').sort_values( by=df.columns[-1], ascending=False).transpose().reset_index().melt( id_vars='index', value_name='Qty').rename( columns={ 'index': 'Date', 'Country/Region': 'Country_Region' }).set_index('Date')) df_countries.index: DateTimeIndex = [ datetime.strptime(date, '%m/%d/%y') for date in df_countries.index ] # Convert data to "wide format" since hvplot does not work with "long format" df_countries_wide = df_countries.pivot(columns='Country_Region', values='Qty') alt_chart: Altair = alt.Chart(df_countries[:iso_date].query( "Country_Region in(@country)").reset_index().rename( columns={'index': 'Date'})).mark_line().encode( x=alt.X(title='Date', field='Date', type='temporal'), y=alt.Y(title='# of ' + confirmed_deaths, field='Qty', type='quantitative', scale=alt.Scale(type=ylog)), color=alt.Color(field='Country_Region', type='nominal', legend=alt.Legend(title="Country/Region")), tooltip=[ alt.Tooltip(field='Country_Region', type='nominal'), alt.Tooltip(field='Qty', type='quantitative'), alt.Tooltip(field='Date', type='temporal') ]) # https://github.com/altair-viz/altair/issues/1812 alt_chart: Altair = alt_chart + alt_chart.mark_point( size=100, opacity=0, tooltip=alt.TooltipContent("data")) # If only one country is selected, then also provide a data table containing counts by date if len(country) == 1: panel_app: PanelRow = pn.Row( # Add Altair chart alt_chart.properties( title='COVID-19 ' + confirmed_deaths, width= 'container', # Have to use 'container' option, otherwise, layout is height=400), # Add hvplot table using data in wide format df_countries_wide[:iso_date].loc[:, country].sort_values( by=df_countries_wide[:iso_date].loc[:, country].columns[0], ascending=False ).merge(df_countries_wide[:iso_date].loc[:, country].sort_values( by=df_countries_wide[:iso_date].loc[:, country].columns[0], ascending=False).diff(), how='inner', left_index=True, right_index=True).reset_index().rename( columns={ 'index': 'Date', f'{country[0]}_x': 'Cum. Qty', f'{country[0]}_y': 'Difference' }).hvplot.table(sortable=True, selectable=True, width=300, height=500)) # Else provide just the Altair line chart else: panel_app: PanelRow = pn.Row( alt_chart.properties(title='COVID-19 ' + confirmed_deaths, width=700, height=400)) return panel_app
def initialize_chart(self): self.tooltip = False x_attr = self.vis.get_attr_by_channel("x")[0] y_attr = self.vis.get_attr_by_channel("y")[0] if (x_attr.data_model == "measure"): agg_title = get_agg_title(x_attr) measure_attr = x_attr.attribute y_attr_field = alt.Y(y_attr.attribute, type= y_attr.data_type, axis=alt.Axis(labelOverlap=True)) x_attr_field = alt.X(x_attr.attribute, type= x_attr.data_type, title=agg_title) y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', axis=alt.Axis(labelOverlap=True))" x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', title='{agg_title}')" if (y_attr.sort=="ascending"): y_attr_field.sort="-x" y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', axis=alt.Axis(labelOverlap=True), sort ='-x')" else: agg_title = get_agg_title(y_attr) measure_attr = y_attr.attribute x_attr_field = alt.X(x_attr.attribute, type = x_attr.data_type,axis=alt.Axis(labelOverlap=True)) y_attr_field = alt.Y(y_attr.attribute,type=y_attr.data_type,title=agg_title) x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', axis=alt.Axis(labelOverlap=True))" y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', title='{agg_title}')" if (x_attr.sort=="ascending"): x_attr_field.sort="-y" x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', axis=alt.Axis(labelOverlap=True),sort='-y')" k=10 topK_code = "" if len(self.data)>k: # Truncating to only top k remaining_bars = len(self.data)-k self.data = self.data.nlargest(k,measure_attr) text = alt.Chart(self.data).mark_text( x=155, y=142, align="right", color = "#ff8e04", fontSize = 11, text=f"+ {remaining_bars} more ..." ) topK_code = f'''text = alt.Chart(visData).mark_text( x=155, y=142, align="right", color = "#ff8e04", fontSize = 11, text=f"+ {remaining_bars} more ..." ) chart = chart + text ''' chart = alt.Chart(self.data).mark_bar().encode( y = y_attr_field, x = x_attr_field ) if (topK_code!=""): chart = chart + text # TODO: tooltip messes up the count() bar charts # Can not do interactive whenever you have default count measure otherwise output strange error (Javascript Error: Cannot read property 'length' of undefined) #chart = chart.interactive() # If you want to enable Zooming and Panning chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null self.code += "import altair as alt\n" # self.code += f"visData = pd.DataFrame({str(self.data.to_dict(orient='records'))})\n" self.code += f"visData = pd.DataFrame({str(self.data.to_dict())})\n" self.code += f''' chart = alt.Chart(visData).mark_bar().encode( y = {y_attr_field_code}, x = {x_attr_field_code}, ) {topK_code} chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null ''' return chart
def initialize_chart(self): # return NotImplemented x_attr = self.vis.get_attr_by_channel("x")[0] y_attr = self.vis.get_attr_by_channel("y")[0] x_attr_abv = str(x_attr.attribute) y_attr_abv = str(y_attr.attribute) if len(x_attr_abv) > 25: x_attr_abv = x_attr.attribute[:15] + "..." + x_attr.attribute[-10:] if len(y_attr_abv) > 25: y_attr_abv = y_attr.attribute[:15] + "..." + y_attr.attribute[-10:] if isinstance(x_attr.attribute, str): x_attr.attribute = x_attr.attribute.replace(".", "") if isinstance(y_attr.attribute, str): y_attr.attribute = y_attr.attribute.replace(".", "") chart = (alt.Chart(self.data).mark_rect().encode( x=alt.X( "xBinStart", type="quantitative", axis=alt.Axis(title=x_attr_abv), bin=alt.BinParams(binned=True), ), x2=alt.X2("xBinEnd"), y=alt.Y( "yBinStart", type="quantitative", axis=alt.Axis(title=y_attr_abv), bin=alt.BinParams(binned=True), ), y2=alt.Y2("yBinEnd"), opacity=alt.Opacity( "count", type="quantitative", scale=alt.Scale(type="log"), legend=None, ), )) chart = chart.configure_scale(minOpacity=0.1, maxOpacity=1) # Setting tooltip as non-null chart = chart.configure_mark(tooltip=alt.TooltipContent("encoding")) chart = chart.interactive() # Enable Zooming and Panning #################################### # Constructing Altair Code String ## #################################### self.code += "import altair as alt\n" # self.code += f"visData = pd.DataFrame({str(self.data.to_dict(orient='records'))})\n" self.code += f"visData = pd.DataFrame({str(self.data.to_dict())})\n" self.code += f""" chart = alt.Chart(visData).mark_rect().encode( x=alt.X('xBinStart', type='quantitative', axis=alt.Axis(title='{x_attr_abv}'), bin = alt.BinParams(binned=True)), x2=alt.X2('xBinEnd'), y=alt.Y('yBinStart', type='quantitative', axis=alt.Axis(title='{y_attr_abv}'), bin = alt.BinParams(binned=True)), y2=alt.Y2('yBinEnd'), opacity = alt.Opacity('count',type='quantitative',scale=alt.Scale(type="log"),legend=None) ) chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null """ return chart
def covid19TimeSeriesByState(covid19_date: Date, state_province: List[str], confirmed_deaths: List[str], ylog: bool = False) -> PanelColumn: """Function that returns a Panel dashboard displaying confirmed COVID-19 cases It is using Panel's "Reactive functions" API: https://panel.holoviz.org/user_guide/APIs.html Parameters ---------- covid19_date : Date End date of data you wish to obtain up to state_province : str State for which you would like to obtain data for (default='Ohio') confirmed_deaths : str Option to choose # of confirmed cases or deaths due to COVID-19 ylog : bool To enable log scaling or not on y-axis. Log scale can be useful to easily discern growth rate. Returns ------- Panel object """ # Panel's date widget returns date in ISO-8601 format iso_date: str = covid19_date.strftime('%Y-%m-%d') # Get geo json data at state county level based on FIPS with urlopen( 'https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json' ) as response: geo_data: dict = json.load(response) # Source of COVID-19 data # To leverage Plotly's choropleth_mapbox function, need to have FIPS as fixed length(n=5) values consisting of leading zeros if confirmed_deaths == 'Confirmed Cases': url: str = 'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_US.csv' else: url: str = 'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_deaths_US.csv' df: DataFrame = pd.read_csv(url, converters={ 'FIPS': lambda x: int(float(x)) if x != '' else x }).query("FIPS != ''") df['FIPS']: Series = df['FIPS'].astype('str').str.zfill(5) if 'Population' in df.columns: df.drop(columns='Population', inplace=True) df_by_state: DataFrame = (df.drop(columns=[ 'UID', 'iso2', 'iso3', 'code3', 'FIPS', 'Admin2', 'Country_Region', 'Lat', 'Long_', 'Combined_Key' ]).groupby('Province_State').agg('sum').transpose().reset_index().melt( id_vars='index', var_name='State_Province', value_name='Qty_Confirmed').rename(columns={ 'index': 'Date' }).set_index('Date')) df_by_state.index: DateTimeIndex = [ datetime.strptime(date, '%m/%d/%y') for date in df_by_state.index ] # Prepare data for hvplot tables # https://stackoverflow.com/questions/53052914/selecting-non-adjacent-columns-by-column-number-pandas df_by_state_ts: DataFrame = df.iloc[:, np.r_[6, 11:df.shape[1]]].groupby( 'Province_State').agg('sum').transpose() df_by_counties: DataFrame = df.query("not Lat==0").iloc[:, [6, 10, -1]] # Prepare data for Plotly choropleth map df_choropleth: DataFrame = df.iloc[:, np.r_[4, 5, 6, df.shape[1] - 1]] df_choropleth: DataFrame = df_choropleth.rename(columns={ df_choropleth.columns[3]: 'Confirmed_Cases', 'Admin2': 'County' }) # Initialize plotly choropleth map plotly_chart: Plotly = px.choropleth_mapbox( df_choropleth.query("Province_State in@state_province"), geojson=geo_data, locations='FIPS', color='Confirmed_Cases', color_discrete_map="Viridis", mapbox_style="carto-positron", zoom=3.5, center={ "lat": 37.0902, "lon": -95.7129 }, opacity=0.5, hover_name='County', width=1400, height=700) plotly_chart.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0}) # Initialize altair chart alt_chart: Altair = alt.Chart(df_by_state[:iso_date].query( "State_Province in(@state_province)").reset_index().rename( columns={'index': 'Date'})).mark_line().encode( x=alt.X(title='Date', field='Date', type='temporal'), y=alt.Y(title='# of ' + confirmed_deaths, field='Qty_Confirmed', type='quantitative', scale=alt.Scale(type=ylog)), color=alt.Color(field='State_Province', type='nominal', legend=alt.Legend(title="State/Province")), tooltip=[ alt.Tooltip(field='State_Province', type='nominal'), alt.Tooltip(field='Qty_Confirmed', type='quantitative'), alt.Tooltip(field='Date', type='temporal') ]) # https://github.com/altair-viz/altair/issues/1812 alt_chart: Altair = alt_chart + alt_chart.mark_point( size=100, opacity=0, tooltip=alt.TooltipContent("data")) # If only one state is selected, then also provide data tables containing counts by counties, by date, and counties choropleth map if len(state_province) == 1: # Layout the panel app such that the column will consist of a row of line chart, data table by counties, and data table by state # then a plotly choropleth below them panel_app: PanelColumn = pn.Column( pn.Row( # Add Altair line chart of cum. covid19 cases or deaths alt_chart.properties( title='COVID-19 ' + confirmed_deaths, width= 'container', # Have to use 'container' option, otherwise, layout is height=400), # A data table of counts by counties df_by_counties.query( "Province_State == @state_province").sort_values( by=df_by_counties.columns[2], ascending=False).drop( columns='Province_State', axis='columns').rename( columns={ 'Combined_Key': 'County', df_by_counties.columns[2]: 'Qty as of ' + df_by_counties.columns[2] }).hvplot.table(sortable=True, selectable=True, width=250, height=500), # A data table of counts by state with difference between rows (df_by_state_ts.loc[:, state_province].sort_values( by=state_province, ascending=False).rename( columns={ 'index': 'Date', state_province[0]: 'Cum. Qty' }) ).merge( (df_by_state_ts.loc[:, state_province].sort_values( by=state_province, ascending=False).rename( columns={ 'index': 'Date', state_province[0]: 'Cum. Qty' })).diff(), how='inner', left_index=True, right_index=True).rename(columns={ 'Cum. Qty_x': 'Cum. Qty', 'Cum. Qty_y': 'Difference' }).reset_index().rename(columns={ 'index': 'Date' }).hvplot.table(sortable=True, selectable=True, width=300, height=500), width=1200, sizing_mode='stretch_width'), # A plotly choropleth Figure plotly_chart) # if more than one state is chosen, just provide Altair line chart, as it would not be feasible # to try to provide data tables and choropleth maps for several states else: panel_app: PanelColumn = pn.Column( pn.Row( # Add Altair line chart of cum. covid19 cases alt_chart.properties(title='COVID-19 ' + confirmed_deaths, width=700, height=400))) return panel_app
def plotAltairLineChart(self): """ Returns an Altair line chart. Returns ------- An Altair line chart. """ df = self.getData(self.confirmed_deaths) iso_date: str = self.covid19_date.strftime('%Y-%m-%d') # Altair requires dataframe to be in "long format" df_countries = (df.drop(columns=['Province/State', 'Lat', 'Long']) .groupby('Country/Region').agg('sum') .sort_values(by=df.columns[-1], ascending=False) .transpose() .reset_index() .melt(id_vars='index', value_name='Qty') .rename(columns={'index': 'Date', 'Country/Region': 'Country_Region' } ) .set_index('Date') ) # Make index values actual datetime objects so that we can # leverage Panda's date filtering API df_countries.index = [datetime.strptime(day, '%m/%d/%y') for day in df_countries.index ] alt_chart = alt.Chart(df_countries[: iso_date] .query("Country_Region in(@self.country)") .reset_index() .rename(columns={'index': 'Date'}) ).mark_line().encode( x=alt.X(title='Date', field='Date', type='temporal'), y=alt.Y(title='# of ' + self.confirmed_deaths, field='Qty', type='quantitative', scale=alt.Scale(type=self.ylog) ), color=alt.Color(field='Country_Region', type='nominal', legend=alt.Legend(title="Country/Region") ), tooltip=[alt.Tooltip(field='Country_Region', type= 'nominal'), alt.Tooltip(field='Qty', type= 'quantitative'), alt.Tooltip(field='Date', type= 'temporal') ] ) # To create filled circles in the legend per # https://github.com/altair-viz/altair/issues/1206 points = alt.Chart(df_countries[: iso_date] .query("Country_Region in(@self.country)") .reset_index() .rename(columns={'index': 'Date'}) ).mark_circle(size=0).encode( color='Country_Region' ) # To add hover tips, but make it less sensitive per # https://github.com/altair-viz/altair/issues/1812 tooltips = alt_chart.mark_point(size=100, opacity=0, tooltip=alt.TooltipContent("data") ) alt_chart = alt_chart + points + tooltips return alt_chart.properties( title='COVID-19 ' + self.confirmed_deaths, width='container', height=400 )
columns={ 0: "correlation", "level_0": "first", "level_1": "second" }) correlation_data["correlation_label"] = correlation_data["correlation"].map( "{:.2f}".format) base_correlation_plot = altair.Chart(correlation_data).encode(x="second:O", y="first:O") # Text layer with correlation labels # Colors are for easier readability correlation_plot_text = base_correlation_plot.mark_text( tooltip=altair.TooltipContent("encoding")).encode( text="correlation_label", color=altair.condition(altair.datum.correlation > 0.5, altair.value("white"), altair.value("black"))) correlation_plot = base_correlation_plot.mark_rect( tooltip=altair.TooltipContent("encoding")).encode(color=altair.Color( "correlation:Q", scale=altair.Scale(domain=[-1, 0, 1], range=["DarkBlue", "White", "DarkRed"], type="linear"))) CORRELATION_MATRIX = correlation_plot + correlation_plot_text (CORRELATION_MATRIX_COLUMN, CORRELATION_OBSERVATION_COLUMN) = streamlit.columns([2, 1])