首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Plotly box p值重要注解

Plotly box p值重要注解
EN

Stack Overflow用户
提问于 2021-05-12 22:05:20
回答 2查看 356关注 0票数 1

我已经开始使用并且喜欢用绘图式的盒子图来表示我的数据。然而,我很难找到一种方法来对比这两组人。在使用Plotly时,有没有办法在数据之间引入统计意义上的比较?我想创建像这样的图表:

其中*对应p值< 0.05,ns (不显著)对应p值> 0.05。我发现使用scipy.stats.ttest_ind()stats.ttest_ind_from_stats() one可以很容易地找到两个分布的p值。

我还没有在网上找到任何相关的帖子,我认为这是一个相当有用的实现,所以任何帮助都将不胜感激!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-05-18 16:05:47

如果有人觉得它有用,我写了这个函数add_p_value_annotation。它创建一个括号注释,并指定两个带星号的框图之间的p值。当你的图中有子图时,它也应该起作用。

代码语言:javascript
运行
复制
def add_p_value_annotation(fig, array_columns, subplot=None, _format=dict(interline=0.07, text_height=1.07, color='black')):
    ''' Adds notations giving the p-value between two box plot data (t-test two-sided comparison)
    
    Parameters:
    ----------
    fig: figure
        plotly boxplot figure
    array_columns: np.array
        array of which columns to compare 
        e.g.: [[0,1], [1,2]] compares column 0 with 1 and 1 with 2
    subplot: None or int
        specifies if the figures has subplots and what subplot to add the notation to
    _format: dict
        format characteristics for the lines

    Returns:
    -------
    fig: figure
        figure with the added notation
    '''
    # Specify in what y_range to plot for each pair of columns
    y_range = np.zeros([len(array_columns), 2])
    for i in range(len(array_columns)):
        y_range[i] = [1.01+i*_format['interline'], 1.02+i*_format['interline']]

    # Get values from figure
    fig_dict = fig.to_dict()

    # Get indices if working with subplots
    if subplot:
        if subplot == 1:
            subplot_str = ''
        else:
            subplot_str =str(subplot)
        indices = [] #Change the box index to the indices of the data for that subplot
        for index, data in enumerate(fig_dict['data']):
            #print(index, data['xaxis'], 'x' + subplot_str)
            if data['xaxis'] == 'x' + subplot_str:
                indices = np.append(indices, index)
        indices = [int(i) for i in indices]
        print((indices))
    else:
        subplot_str = ''

    # Print the p-values
    for index, column_pair in enumerate(array_columns):
        if subplot:
            data_pair = [indices[column_pair[0]], indices[column_pair[1]]]
        else:
            data_pair = column_pair

        # Mare sure it is selecting the data and subplot you want
        #print('0:', fig_dict['data'][data_pair[0]]['name'], fig_dict['data'][data_pair[0]]['xaxis'])
        #print('1:', fig_dict['data'][data_pair[1]]['name'], fig_dict['data'][data_pair[1]]['xaxis'])

        # Get the p-value
        pvalue = stats.ttest_ind(
            fig_dict['data'][data_pair[0]]['y'],
            fig_dict['data'][data_pair[1]]['y'],
            equal_var=False,
        )[1]
        if pvalue >= 0.05:
            symbol = 'ns'
        elif pvalue >= 0.01: 
            symbol = '*'
        elif pvalue >= 0.001:
            symbol = '**'
        else:
            symbol = '***'
        # Vertical line
        fig.add_shape(type="line",
            xref="x"+subplot_str, yref="y"+subplot_str+" domain",
            x0=column_pair[0], y0=y_range[index][0], 
            x1=column_pair[0], y1=y_range[index][1],
            line=dict(color=_format['color'], width=2,)
        )
        # Horizontal line
        fig.add_shape(type="line",
            xref="x"+subplot_str, yref="y"+subplot_str+" domain",
            x0=column_pair[0], y0=y_range[index][1], 
            x1=column_pair[1], y1=y_range[index][1],
            line=dict(color=_format['color'], width=2,)
        )
        # Vertical line
        fig.add_shape(type="line",
            xref="x"+subplot_str, yref="y"+subplot_str+" domain",
            x0=column_pair[1], y0=y_range[index][0], 
            x1=column_pair[1], y1=y_range[index][1],
            line=dict(color=_format['color'], width=2,)
        )
        ## add text at the correct x, y coordinates
        ## for bars, there is a direct mapping from the bar number to 0, 1, 2...
        fig.add_annotation(dict(font=dict(color=_format['color'],size=14),
            x=(column_pair[0] + column_pair[1])/2,
            y=y_range[index][1]*_format['text_height'],
            showarrow=False,
            text=symbol,
            textangle=0,
            xref="x"+subplot_str,
            yref="y"+subplot_str+" domain"
        ))
    return fig

如果我们现在创建一个图形并测试函数,我们应该会得到以下输出。

代码语言:javascript
运行
复制
from scipy import stats
import plotly.express as px
import plotly.graph_objects as go
import numpy as np

tips = px.data.tips()

fig = go.Figure()
for day in ['Thur','Fri','Sat','Sun']:
    fig.add_trace(go.Box(
        y=tips[tips['day'] == day].total_bill,
        name=day,
        boxpoints='outliers'
    ))
fig = add_p_value_annotation(fig, [[0,1], [0,2], [0,3]])
fig.show()

票数 1
EN

Stack Overflow用户

发布于 2021-05-13 17:28:06

在Plotly中,对于这种特定的东西,绝对没有内置的方法。

您需要做的是自己创建括号注释,方法是使用fig.add_shape方法三次创建三条不同的线,其中x值对应于您正在比较的两个条形图,而y值对应于此括号形状的高度的相对较小的变化(使用在绘图的paper coordinates中给定的y坐标)。由于您希望括号注释位于打印上方,因此我们将处理高于1的图纸坐标,例如[1.02, 1.03]的y_range。

然后,我们需要使用fig.add_annotation方法将文本(可以是'*''ns',这取决于t-test的p值)注释到这个括号注释的上方。可以在text and annotations documentation中找到更深入的解释。

为了便于重用,我将整个过程封装在一个函数中,该函数接收您想要比较的两天的列表,以及您希望将括号注释限制在其中的纸张坐标中的y范围。

代码语言:javascript
运行
复制
from scipy import stats
import plotly.express as px
import plotly.graph_objects as go

tips = px.data.tips()
# stats.ttest_ind(tips[tips['day']=='Thur'].total_bill,tips[tips['day']=='Fri'].total_bill)
# stats.ttest_ind(tips[tips['day']=='Thur'].total_bill,tips[tips['day']=='Sat'].total_bill)

fig = go.Figure()
for day in ['Thur','Fri','Sat','Sun']:
    fig.add_trace(go.Box(
        y=tips[tips['day'] == day].total_bill,
        name=day,
        boxpoints='outliers'
    ))

def add_pvalue_annotation(days, y_range, symbol=''):
    """
    arguments:
    days --- a list of two different days e.g. ['Thur','Sat']
    y_range --- a list of y_range in the form [y_min, y_max] in paper units
    """
    pvalue = stats.ttest_ind(
        tips[tips['day']==days[0]].total_bill,
        tips[tips['day']==days[1]].total_bill)[1]
    # print(pvalue)
    if pvalue >= 0.05:
        symbol = 'ns'
    if pvalue < 0.05:
        symbol = '*'
    fig.add_shape(type="line",
        xref="x", yref="paper",
        x0=days[0], y0=y_range[0], x1=days[0], y1=y_range[1],
        line=dict(
            color="black",
            width=2,
        )
    )
    fig.add_shape(type="line",
        xref="x", yref="paper",
        x0=days[0], y0=y_range[1], x1=days[1], y1=y_range[1],
        line=dict(
            color="black",
            width=2,
        )
    )
    fig.add_shape(type="line",
        xref="x", yref="paper",
        x0=days[1], y0=y_range[1], x1=days[1], y1=y_range[0],
        line=dict(
            color="black",
            width=2,
        )
    )
    ## add text at the correct x, y coordinates
    ## for bars, there is a direct mapping from the bar number to 0, 1, 2...
    bar_xcoord_map = {x: idx for idx, x in enumerate(['Thur','Fri','Sat','Sun'])}
    fig.add_annotation(dict(font=dict(color="black",size=14),
        x=(bar_xcoord_map[days[0]] + bar_xcoord_map[days[1]])/2,
        y=y_range[1]*1.03,
        showarrow=False,
        text=symbol,
        textangle=0,
        xref="x",
        yref="paper"
    ))

add_pvalue_annotation(['Thur','Sun'],[1.01,1.02])
add_pvalue_annotation(['Thur','Sat'],[1.05,1.06])

fig.show()

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67505252

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档