Python 中具有堆积范围的圆形可视化

Circular Visualization in Python with Piled Ranges

如何在 matplotlib(或其他包)中创建循环可视化(基因组表示)并堆积具有百分比范围的片段。比如我有3个基因,我想把这3个基因堆到一个长度为0-1的圆形图上。需要一些偏移量来区分各个范围。

基因 1:.1 - .25
基因 2:.15 - .3
基因 3:.6 - .68

下面的期望输出:

我会说使用 Matplotlib 绘制它并不容易,但我认为您会对您在问题中声称的“或其他包”的其他库感到满意。

我将展示我使用 Plotly 绘制它的方法。您可以简单地 运行 pip install plotly 来拥有这个库。

我会用极轴来画圆图,这样就方便多了。

首先,让我们定义一个函数,用于获取在特定时间重复的半径 r 以及 theta 值。请注意 num_points 是一个常量值,如果您将其设置得更高,它会使您的图表更“平滑”。

import plotly.graph_objects as go
import numpy as np

def get_theta(pct, num_points=360):
  start = pct[0] * 360
  length = (pct[1] - pct[0]) * 360
  step = 360 / num_points
  return np.arange(start, start + length + step, step)

我会硬编码你的基因列表。在你的情况下,我假设你不必这样做:)

# Hard-code your gene list
gene_list = [
  [0.1, 0.25],
  [0.15, 0.3],
  [0.6, 0.68]
]

然后是绘制圆形图表的代码。

# Get radial scale
max_r = 1 + (len(gene_list) + 1) * 0.1

# Create a figure
fig = go.Figure()

# Create the main circle
fig.add_trace(go.Scatterpolar(
  r=[1]*360,
  theta=get_theta([0, 1]),
  mode='lines',
  line_color='black',
  line_width=3
))

# Create the zero indicator
fig.add_trace(go.Scatterpolar(
  r=[1, max_r - 0.1],
  theta=[0, 0],
  mode='lines',
  line_color='black',
  line_width=3
))

# Loop the gene list to add all the gene cirles 
for index, circle in enumerate(gene_list):
  fig.add_trace(go.Scatterpolar(
    r = [1 + (index + 1) * 0.1] * 360,
    theta = get_theta(circle),
    mode='lines',
    line_width=3
  ))

# Configure the layout based on the requirements.
fig.update_layout(
  polar=dict(
      angularaxis=dict(
        rotation=90,
        direction="clockwise",
        showticklabels=True,
        showgrid=False
      ),
      radialaxis=dict(
        range=[0, 1 + (len(gene_list) + 1) * 0.1],
        showticklabels=False,
        visible=False
      )
  ),
  paper_bgcolor='white'
)

# Show the figure
fig.show()

这是代码生成的图表。

当然我可以看到仍然存在问题。每个“基因”段不会在相同的半径上呈现。完全复制您的示例并非不可能,但显然并不容易。希望这对你来说足够了。如果它对你来说真的很重要。可能值得尝试使用小于 0.1 的步骤(在我的代码中找到 0.1 并替换它)。

仅供参考,这是使用“仅”matplotlib:

的方法
import numpy as np
import matplotlib.pyplot as plt

data={
    0:{'name':'Gene 1', 'start': .10, 'end': .25, 'offset': 0.1, 'color': 'blue'},
    1:{'name':'Gene 2', 'start': .15, 'end': .30, 'offset': 0.2, 'color': 'orange'},
    2:{'name':'Gene 3', 'start': .60, 'end': .68, 'offset': 0.1, 'color': 'green'}
}


## setup the figure:
fig = plt.figure()
ax = plt.subplot(111, projection='polar')
ax.set_theta_direction(-1) # make it go clockwise
ax.set_theta_zero_location('N') # put "0" at top
# remove the labels
ax.set_xticks([]) 
ax.set_yticks([])
ax.spines['polar'].set_visible(False)
LW = 3.0 ## setting the linewidth globally for fine-tuning


def add_bg_circle(ax):
    """ adds the central circle with top notch """
    # the circle
    circle_x = np.linspace(0,2*np.pi,200)
    circle_y = np.array([1.0 for x in circle_x])
    ax.plot(circle_x,circle_y,c="k",linewidth=LW)
    # the top notch
    line_y = np.linspace(1.0,1.1) ## change length of the top line here
    line_x = np.array([0 for y in line_y])
    ax.plot(line_x,line_y,c="k",linewidth=LW)
    # the text
    ax.text(0.0,1.15,"0",ha="center",va="center")
    
add_bg_circle(ax)    

## plot the line segments:
for (k,v) in data.items():
    xs = np.linspace(v['start']*2*np.pi , v['end']*2*np.pi, 200)
    ys = np.array([(1.0 + v['offset']) for x in xs])

    ax.plot(xs, ys, linewidth=LW, label=v['name'])

_inner_lim = 0.5 # keep this below the value for the main circle at 1.0
_outer_lim = 1.3 # adjust to include all plotted segments
ax.set_ylim(_inner_lim,_outer_lim) 

# plt.legend() # optional
# plt.savefig("dna_circle.png")
plt.show()

产量: