2

I want to create a Contour-Enhanced funnel plot (not a funnel chart),to test the small-study effect in meta-analysis like the following:

![R funnelplot

The above plot was created in R, which has several packages for doing this. However, I want to create it in Python, using matplotlib and pandas. How can I do this? I have tried Pythonmeta package and got this funnel plot enter image description here

This is the source code of the package:

    def __init__(self,size=[6,6],dpi=80): #set figure
        self.size=size #inchs
        self.dpi=dpi   #default:80pts
        self.title="Meta-analysis Results "  
        self.nototal=False
        plt.rcParams['font.sans-serif']=['SimHei'] 
        plt.rcParams['axes.unicode_minus']=False
        logger.info("Load Fig().")
    def funnel (self,effs):
        myfig = Fig_Funnel (self.size,self.dpi,effs)
        return myfig```
    def Fig_Funnel (size,dpi,effs):
        myfig = plt.figure(linewidth=1,figsize=size,dpi=dpi)
        myfig.set_size_inches(size)
        x=[];y=[]
        for i in range(1,len(effs)):
            x.append(effs[i][1])
            y.append(effs[i][6])
        lbl,=plt.plot(x,y,"o", lw=1)

    
        ymax=max(y)+0.2
        plt.ylim(ymax,0)
        plt.plot([effs[0][1],effs[0][1]],[0,ymax],color="blue", linestyle="--",
        lw=1)
        plt.plot([effs[0][1],effs[0][1]-1.96*ymax],[0,ymax],color="blue",
        linestyle="--", lw=1)
        plt.plot([effs[0][1],effs[0][1]+1.96*ymax],[0,ymax],color="blue",
        linestyle="--", lw=1)
        ax = gca()
        ax.spines['right'].set_color('none')
        ax.spines['top'].set_color('none')
        ax.xaxis.set_ticks_position('bottom')
        ax.yaxis.set_ticks_position('left')

        plt.xlabel("Effect Size")
        plt.ylabel("Standard Error")
        myfig.tight_layout() 
        return myfig
Safoora mk
  • 305
  • 2
  • 12
  • Hi Safoora, please tell us what you have tried so far and how the source plot was created, e.g. show us code und data. People gladly help, but do not want to do your work – dube Dec 17 '20 at 19:44
  • 1
    Thank you for the comment.I do not want anybody do my work.I searched a lot for package to create funnel plot in Python but there is not any one.I just want some body give me a clue and tell me if it is possible or not.I have tried Pymeta package for my meta-analysis ,it results in a simple funnel plot ,I have tried to change the source code but could not change it to give me a contour-enhanced funnel plot – Safoora mk Dec 17 '20 at 20:25
  • @dube I think I cannot communicate it well – Safoora mk Dec 17 '20 at 20:42
  • @dube Hi. should I add more details? – Safoora mk Dec 19 '20 at 07:17
  • Hey Safoora. Sorry, it's not my topic, I can't help you. It's already better like this, but who knows – dube Dec 19 '20 at 21:37

2 Answers2

4

Finally, I created it myself.:)

First of all, to understand the concept of a Contour-enhanced funnel plot, I recommend reading Jaime L Peters paper.

To create the plot, I used the following code

def CEfunnelplot(rults):      #to draw contour enhanced funnel plot

fig=plt.figure(figsize=(9,8))
ax=gca()
x=logES(rults)
y=SE(rults)      

plt.xlim(min(x)-5,max(x)+2)  #the xlim can change manually
plt.ylim(max(y)+0.1,0)
ax.set_facecolor(color="whitesmoke")
tes=(rults[0][1])
logtes=math.log(rults[0][1])


plt.plot ([0,(-2.58*max(y))],[0,max(y)], linestyle="None", lw=1)
plt.plot ([0,(2.58*max(y))],[0,max(y)], linestyle="None", lw=1)

trianglex = [ -2.58*max(y), 0, 2.58*max(y)] 
triangley = [ max(y),0, max(y)] 
for i in range(3):
    plt.plot(trianglex, triangley,color="lightgray")
plt.fill(trianglex, triangley,"lightgray")

plt.plot ([0,(-1.96*max(y))],[0,max(y)], linestyle="None", lw=1)
plt.plot ([0,(1.96*max(y))],[0,max(y)], linestyle="None", lw=1)

trianglex = [ -1.96*max(y), 0,1.96*max(y)] 
triangley = [ max(y),0, max(y)] 
for i in range(3):
    plt.plot(trianglex, triangley,color="darkgrey")
plt.fill(trianglex, triangley,"darkgrey")


plt.plot ([0,(-1.65*max(y))],[0,max(y)], linestyle="None", lw=1)
plt.plot ([0,(1.65*max(y))],[0,max(y)], linestyle="None", lw=1)

trianglex = [ -1.65*max(y), 0,1.65*max(y)] 
triangley = [ max(y),0, max(y)] 
for i in range(3):
    plt.plot(trianglex, triangley,color="white")
plt.fill(trianglex, triangley,"white")

plt.plot ([logtes,logtes],[0,max(y)],color="blue", linestyle="--", lw=1)
plt.plot ([logtes,logtes-1.96*max(y)],[0,max(y)],color="blue", linestyle="--", lw=1)
plt.plot ([logtes,logtes+1.96*max(y)],[0,max(y)],color="blue", linestyle="--", lw=1)
plt.plot(x,y,"o",lw=1,color="k")

ax = gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
colors = ['whitesmoke', 'lightgray', 'darkgrey',"white"]
lines =[Line2D([0], [0], color=c, linewidth=3, linestyle='solid') for c in colors]
labels = ["p < 1%","1%< p <5%","5%< p <10%","p > 10%"]
plt.legend(lines, labels,shadow=True)    

plt.xlabel("logRR",fontsize=14 )
plt.ylabel("Standard Error",fontsize=14,rotation=90 )
plt.savefig("Contour Enhanced Funnel plot.jpg")

I used This sample code for creating the legend and This code to color the plot. The result is as below:

Contour-Enhanced funnel plot

If you like to change the marker of legend to "square", you can change the code as below

lines =[Line2D([0], [0],color=c,linewidth=3,linestyle='None',marker="s") for c in colors]

Figure with square markers for legend

Safoora mk
  • 305
  • 2
  • 12
1

So, based on @Safoora 's solution I created a function which generates a simplified funnel plot similar to what we can obtain in R. The input parameter is a Pandas DataFrame with effect sizes and variability columns. It also allows to customize the plot. I have compared the result with that from R jamovi MAJOR module and it looks very similar. Feel free to make any necessary changes.

def funnel_plot(data, mid_value=0, es='g', var='SE', figsize=(9, 6),
                x_adj=(1.5, 1), y_adj=(0.01, -0.01), xlabel="Observed Outcome",
                ylabel="Standard Error", colors=['whitesmoke','lightgray','darkgrey',"white"], 
                gridcol='white', filename='funnel1', filetype='png',
                filedpi=100)->None:
    
    """Function to create a funnel plot for publication bias analysis.
       
       Parameters
       ----------
       data: DataFrame, required
           Includes columns with effect sizes and variability
           specified as column names in parameters 'es' and 'var'
       mid_value: float, optional
           This can be an estimate of the overall effect
       ... 
    """

    fig, ax = plt.subplots(figsize=figsize)

    # Prepare data
    x = data[es].to_numpy()
    y = np.round(data[var].to_numpy(), 3)
    mid = mid_value

    ax.set_xlim(np.min(x) - x_adj[0], np.max(x) + x_adj[1])
    ax.set_ylim(np.max(y) + y_adj[0], y_adj[1])
    ax.set_facecolor(color=colors[0])
    
    ax.grid(axis='y', zorder=0, color=gridcol)

    # Contours
    ax.plot ([mid,(-2.58 * np.max(y))+mid],[0, np.max(y)+mid], linestyle="none", lw=1)
    ax.plot ([mid,(2.58 * np.max(y))+mid],[0, np.max(y)+mid], linestyle="none", lw=1)

    trianglex = [ -2.58 * np.max(y)+mid, mid, 2.58 * np.max(y)+mid]
    triangley = [ np.max(y), 0, np.max(y)]
    for i in range(3):
        ax.plot(trianglex, triangley,color=colors[1], zorder=3)
    ax.fill(trianglex, triangley, colors[1], zorder=3)

    ax.plot ([mid,(-1.96 * np.max(y))+mid], [0, np.max(y)+mid], linestyle="None", lw=1)
    ax.plot ([mid,(1.96 * np.max(y))+mid], [0, np.max(y)+mid], linestyle="None", lw=1)

    trianglex = [ -1.96 * np.max(y)+mid, mid, 1.96 * np.max(y)+mid]
    triangley = [ np.max(y), 0, np.max(y)]
    for i in range(3):
        ax.plot(trianglex, triangley,color=colors[2], zorder=3)
    ax.fill(trianglex, triangley, colors[2], zorder=3)

    ax.plot ([0,(-1.65 * np.max(y))+mid],[0, np.max(y)+mid], linestyle="None", lw=1)
    ax.plot ([0,(1.65 * np.max(y))+mid],[0, np.max(y)+mid], linestyle="None", lw=1)

    trianglex = [ -1.65 * np.max(y)+mid, mid, 1.65 * np.max(y)+mid]
    triangley = [ np.max(y), 0, np.max(y)]
    for i in range(3):
        ax.plot(trianglex, triangley,color=colors[3], zorder=3)
    ax.fill(trianglex, triangley, colors[3], zorder=3)

    ax.axvline(x=mid, color='black', linestyle='dotted', linewidth=0.8, ymin=0, ymax=1, zorder=3)

    # Actual data plotting
    ax.scatter(x, y, marker="o", color="black", zorder=3)

    ax.spines[['top', 'right']].set_visible(False)
    ax.set_xlabel(xlabel, fontsize=12)
    ax.set_ylabel(ylabel, fontsize=12)
    plt.show()

    # Save image
    if filename and filetype:
        fig.savefig(f"{filename}.{filetype}", dpi=filedpi, format=filetype)
        print('Saving image file')

    return None

Then:


funnel_plot(df, mid_value=my_overall_effect)

and the result: enter image description here

marles77
  • 11
  • 2