Plotly:如何在具有分面的 Plotly Express 图中隐藏轴标题?

14
使用Plotly Express在多面板图表中隐藏重复的坐标轴标题是否有简单的方法?我尝试过设置。
visible=True
在下面的代码中,进行了重复的轴标题隐藏(即值),但这也隐藏了y轴刻度标签。理想情况下,我希望在一般情况下将隐藏重复轴标题设置为分面图的默认设置(或者更好的是,仅默认显示整个分面图的单个x和y轴标题)。
以下是测试代码:
import pandas as pd
import numpy as np
import plotly.express as px
import string

# create a dataframe
cols = list(string.ascii_letters)
n = 50

df = pd.DataFrame({'Date': pd.date_range('2021-01-01', periods=n)})

# create data with vastly different ranges
for col in cols:
    start = np.random.choice([1, 10, 100, 1000, 100000])
    s = np.random.normal(loc=0, scale=0.01*start, size=n)
    df[col] = start + s.cumsum()

# melt data columns from wide to long
dfm = df.melt("Date")

fig = px.line(
    data_frame=dfm,
    x = 'Date',
    y = 'value',
    facet_col = 'variable',
    facet_col_wrap=6,
    facet_col_spacing=0.05,
    facet_row_spacing=0.035,
    height = 1000,
    width = 1000,
    title = 'Value vs. Date'
)

fig.update_yaxes(matches=None, showticklabels=True, visible=True)
fig.update_annotations(font=dict(size=16))
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))

这里输入图片描述

最终代码(被接受的答案)。注意 plotly >= 4.9

import pandas as pd
import numpy as np
import plotly.express as px
import string
import plotly.graph_objects as go

# create a dataframe
cols = list(string.ascii_letters)
n = 50

df = pd.DataFrame({'Date': pd.date_range('2021-01-01', periods=n)})

# create data with vastly different ranges
for col in cols:
    start = np.random.choice([1, 10, 100, 1000, 100000])
    s = np.random.normal(loc=0, scale=0.01*start, size=n)
    df[col] = start + s.cumsum()

# melt data columns from wide to long
dfm = df.melt("Date")

fig = px.line(
    data_frame=dfm,
    x = 'Date',
    y = 'value',
    facet_col = 'variable',
    facet_col_wrap=6,
    facet_col_spacing=0.05,
    facet_row_spacing=0.035,
    height = 1000,
    width = 1000,
    title = 'Value vs. Date'
)

fig.update_yaxes(matches=None, showticklabels=True, visible=True)
fig.update_annotations(font=dict(size=16))
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))

# hide subplot y-axis titles and x-axis titles
for axis in fig.layout:
    if type(fig.layout[axis]) == go.layout.YAxis:
        fig.layout[axis].title.text = ''
    if type(fig.layout[axis]) == go.layout.XAxis:
        fig.layout[axis].title.text = ''
        
# keep all other annotations and add single y-axis and x-axis title:
fig.update_layout(
    # keep the original annotations and add a list of new annotations:
    annotations = list(fig.layout.annotations) + 
    [go.layout.Annotation(
            x=-0.07,
            y=0.5,
            font=dict(
                size=16, color = 'blue'
            ),
            showarrow=False,
            text="single y-axis title",
            textangle=-90,
            xref="paper",
            yref="paper"
        )
    ] +
    [go.layout.Annotation(
            x=0.5,
            y=-0.08,
            font=dict(
                size=16, color = 'blue'
            ),
            showarrow=False,
            text="Dates",
            textangle=-0,
            xref="paper",
            yref="paper"
        )
    ]
)

fig.show()
2个回答

9

这个答案有五个部分:

  1. 隐藏子图标题(不确定是否需要...)
  2. 使用 fig.layout[axis].tickfont = dict(color = 'rgba(0,0,0,0)') 隐藏y轴刻度值
  3. 使用 go.layout.Annotation(xref="paper", yref="paper") 设置单一的坐标轴标签
  4. plotly图表
  5. 最后给出完整的代码片段

一个非常重要的收获是,您可以使用 plotly.graph_object 引用编辑任何使用 px 函数生成的元素,例如 go.layout.XAxis


1. 隐藏子图标题

如果您对设置好的 fig 没有其他问题,只需包含以下内容:

for anno in fig['layout']['annotations']:
    anno['text']=''
    
fig.show()

2. 隐藏y轴文本

您可以在循环中使用以下代码将y轴刻度字体设置为透明:

fig.layout[axis].tickfont = dict(color = 'rgba(0,0,0,0)')

下面的代码段中包括了刚才提到的精确一行代码,它可以删除每个子图的y轴标题。

3. 单一轴标签

要删除轴标签并添加单个标签需要更多的工作,但是以下非常灵活的设置可以完全满足您的需求,如果您想以任何方式编辑新标签,这里还提供了更多功能:

# hide subplot y-axis titles and x-axis titles
for axis in fig.layout:
    if type(fig.layout[axis]) == go.layout.YAxis:
        fig.layout[axis].title.text = ''
    if type(fig.layout[axis]) == go.layout.XAxis:
        fig.layout[axis].title.text = ''
        
# keep all other annotations and add single y-axis and x-axis title:
fig.update_layout(
    # keep the original annotations and add a list of new annotations:
    annotations = list(fig.layout.annotations) + 
    [go.layout.Annotation(
            x=-0.07,
            y=0.5,
            font=dict(
                size=16, color = 'blue'
            ),
            showarrow=False,
            text="single y-axis title",
            textangle=-90,
            xref="paper",
            yref="paper"
        )
    ] +
    [go.layout.Annotation(
            x=0.5,
            y=-0.08,
            font=dict(
                size=16, color = 'blue'
            ),
            showarrow=False,
            text="Dates",
            textangle=-0,
            xref="paper",
            yref="paper"
        )
    ]
)

fig.show()

4. 绘图

输入图片说明

5. 完整代码:

import pandas as pd
import numpy as np
import plotly.express as px
import string
import plotly.graph_objects as go

# create a dataframe
cols = list(string.ascii_letters)
cols[0]='zzz'
n = 50

df = pd.DataFrame({'Date': pd.date_range('2021-01-01', periods=n)})

# create data with vastly different ranges
for col in cols:
    start = np.random.choice([1, 10, 100, 1000, 100000])
    s = np.random.normal(loc=0, scale=0.01*start, size=n)
    df[col] = start + s.cumsum()

# melt data columns from wide to long
dfm = df.melt("Date")

fig = px.line(
    data_frame=dfm,
    x = 'Date',
    y = 'value',
    facet_col = 'variable',
    facet_col_wrap=6,
    #facet_col_spacing=0.05,
    #facet_row_spacing=0.035,
    height = 1000,
    width = 1000,
    title = 'Value vs. Date'
)

fig.update_yaxes(matches=None, showticklabels=True, visible=True)
fig.update_annotations(font=dict(size=16))
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))

# subplot titles
for anno in fig['layout']['annotations']:
    anno['text']=''

# hide subplot y-axis titles and x-axis titles
for axis in fig.layout:
    if type(fig.layout[axis]) == go.layout.YAxis:
        fig.layout[axis].title.text = ''
    if type(fig.layout[axis]) == go.layout.XAxis:
        fig.layout[axis].title.text = ''
        
# keep all other annotations and add single y-axis and x-axis title:
fig.update_layout(
    # keep the original annotations and add a list of new annotations:
    annotations = list(fig.layout.annotations) + 
    [go.layout.Annotation(
            x=-0.07,
            y=0.5,
            font=dict(
                size=16, color = 'blue'
            ),
            showarrow=False,
            text="single y-axis title",
            textangle=-90,
            xref="paper",
            yref="paper"
        )
    ] +
    [go.layout.Annotation(
            x=0.5,
            y=-0.08,
            font=dict(
                size=16, color = 'blue'
            ),
            showarrow=False,
            text="Dates",
            textangle=-0,
            xref="paper",
            yref="paper"
        )
    ]
)


fig.show()

感谢您的快速回复。也许我的帖子不够清晰。我只想消除重复的X和Y轴标题(“日期”和“值”),并保留图表的所有其他元素不变。y轴值都是相同的测量单位(例如美元、计数等),但范围却大不相同,因此仍需要刻度标签。我运行了代码,它与发布的屏幕截图不同,更接近所需的输出,但缺少facet标题(字母)并且facet之间的间距不正确(垂直太小,水平太多)。 - Randall Goodwin
好的,稍加调整后,我成功地得到了期望的输出,并将其标记为已解决(并添加完整代码)。我正在使用plotly 4.9,因此解决了间距问题(我没有注意到您注释掉了那些行 :))。FYI-我希望在plotly express中完全完成这个任务,作为更简单的选项(更像R语言中的lattice、ggplot2)。再次感谢您的回复! - Randall Goodwin
@RandallGoodwin 这个问题不太清楚,但我似乎也误读了其中的一些部分。但是不用担心,我解决它时还是学到了很多东西。很高兴最终它能为您工作!感谢您将我的建议标记为所接受的答案。 - vestland
1
在重新阅读了我的原始帖子后,我不确定自己是否理解得很好!您的回复涵盖了所有基本问题,再次感谢! - Randall Goodwin
@RandallGoodwin 没问题!很高兴能帮忙! - vestland

8
作为一个侧记,我发现通过在plotly express调用中使用标签参数并将其提供给带有''值的标签字典的方式,可以更直接地消除轴标签。
这并不会导致整个图形级别上的单个标签。但是,如果图形标题足够描述"Y vs. X",那么也许可以"原谅"缺少轴标签?(或者像 @vestland演示的那样添加)
请注意,您几乎可以消除annoying repeated facet titles,即每个子绘图中都包含“=value”的facet标题。即,如果您向标签字典添加一个条目: 'variable': '' 那么您将只获得facet变量级别,前面带有"=",而不是"variable = variable level",如下图所示。
完整代码:
import pandas as pd
import numpy as np
import plotly.express as px
import string

# create a dataframe
cols = list(string.ascii_letters)
n = 50

df = pd.DataFrame({'Date': pd.date_range('2021-01-01', periods=n)})

# create data with vastly different ranges
for col in cols:
    start = np.random.choice([1, 10, 100, 1000, 100000])
    s = np.random.normal(loc=0, scale=0.01*start, size=n)
    df[col] = start + s.cumsum()

# melt data columns from wide to long
dfm = df.melt("Date")

# make the plot
fig = px.line(
    data_frame=dfm,
    x = 'Date',
    y = 'value',
    facet_col = 'variable',
    facet_col_wrap=6,
    facet_col_spacing=0.05,
    facet_row_spacing=0.035,
    height = 1000,
    width = 1000,
    title = 'Value vs. Date',
    labels = {
        'Date': '',
        'value': '',
        'variable': ''
    }
)

# ensure that each chart has its own y rage and tick labels
fig.update_yaxes(matches=None, showticklabels=True, visible=True)

fig.show()

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接