【问题标题】:matplotlib plot circular daily-cycle diagram (daily polar plot)matplotlib plot 圆形日周期图(日极坐标图)
【发布时间】:2021-04-29 10:22:47
【问题描述】:

我正在尝试将采样策略的每日周期绘制为一种玫瑰图/极坐标图。

我想显示我们的每个实验样本,其中距中心的距离是一天,从 0 开始的角度代表收集该样本的时间。理想情况下,我希望能够通过不同的变量为点着色。

理想的情节应该如下所示:

模拟虚拟数据来解释问题

我有xarray 格式的数据。我们有一个 launchtime 维度,其中包含采集样本的时间,我们希望使用它来绘制一天中的什么时间,然后依次绘制每一天。

import xarray as xr
import numpy as np
import matplotlib.pyplot as plt 
import pandas as pd 
from pandas.tseries.offsets import DateOffset
import matplotlib.dates as mdates
import itertools

value = np.random.normal(size=100)
expected_time = pd.date_range("2000-01-01", freq="180min", periods=100)
# add random offset to simulate being +/- the true expected release time
time_deltas = np.array([DateOffset(minute=max(0, min(int(i), 59))) for i in np.abs(np.random.normal(0, 10, size=100))])
time = [expected_time[i] + time_deltas[i] if (i % 2 == 0) else expected_time[i] - time_deltas[i] for i in range(100)]

df = pd.DataFrame({"launchtime": time, "value": value})
ds = df.set_index("launchtime").to_xarray()
ds = ds.assign_coords(expected_time=("launchtime", expected_time))

到目前为止我的想法

def time_to_angle(dt: pd.Timestamp) -> float:
    SEC_IN_DAY = 86_400
    start_of_day = pd.to_datetime(f"{dt.day}-{dt.month}-{dt.year}")
    delta = (dt - start_of_day)
    n_seconds = delta.seconds
    
    # return angle in degrees
    return (n_seconds / SEC_IN_DAY) * 360


# angle from 0 degrees
angles = [time_to_angle(pd.to_datetime(dt)) for dt in ds.launchtime.values]

# how far along the radius
days = np.arange(np.unique(ds["launchtime.day"].values).size)

# how to plot in polar coordinates? Do I have to draw an x,y grid and plot as a scatter?

任何关于如何解决这个问题的建议都将非常感激!

【问题讨论】:

    标签: python python-3.x pandas matplotlib


    【解决方案1】:

    通过一点高中/中学三角学,它可以很简单地实现为散点图

    • 将一天中的时间视为以弧度计算的角度
    • 将天(几岁)视为半径
    • 然后简化为简单地使用 sin()cos() 来计算 xy坐标
    • 为了更好的测量,决定也给点上色...不太满意显示 24 小时的钟面,模拟时钟只显示 12 小时
    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    import math
    
    fig, ax = plt.subplots(1,1, figsize=(4, 4))
    
    df = pd.DataFrame({"sampledate":pd.date_range("01-apr-2021", "09-apr-2021 23:59", freq="30s")})
    # drop a bit of data so it's not perfect circles...
    df = df.loc[np.random.choice(df.index, int(len(df)/200) )]
    
    df["angle"] = ((df["sampledate"] - df["sampledate"].dt.floor("D")).dt.total_seconds() / (24*60*60)) * 2*math.pi
    df["radius"] = (df["sampledate"] - df["sampledate"].min()).dt.days+1
    
    
    # scatter, radius is how old sample is, angle is time of day
    ax.scatter(x=df["angle"].apply(math.sin) * df["radius"], y=df["angle"].apply(math.cos) * df["radius"], 
               c=np.where(df.sampledate.dt.hour.le(12), "red", "pink"), s=10)
    ax.axis("off")
    
    # draw markers on clock face...
    for h in list(range(0, 24, 3)):
        a = (h/24)*2*math.pi
        x = math.sin(a)*df["radius"].max()
        y = math.cos(a)*df["radius"].max()
        ax.annotate(h, xy=(x, y), xytext=(x*1.1,y*1.1), backgroundcolor="yellow")
    

    【讨论】:

      【解决方案2】:

      我们可以使用projection="polar" 参数到ax = plt.subplot(projection='polar') 参数来创建一个绘图:

      • 角度显示一天中的时间(theta,以弧度定义)
      • 半径显示自第一个样本 (r) 以来的天数。

      我们需要对一天中的时间数据进行一些初始预处理

      from sklearn.preprocessing import LabelEncoder
      
      def time_to_angle(dt: pd.Timestamp) -> float:
          SEC_IN_DAY = 86_400
          start_of_day = pd.to_datetime(f"{dt.day}-{dt.month}-{dt.year}")
          delta = (dt - start_of_day)
          n_seconds = delta.seconds
          
          # return angle in radians
          return (n_seconds / SEC_IN_DAY) * 2 * np.pi
      
      
      # angle from 0 degrees
      angles = [time_to_angle(pd.to_datetime(dt)) for dt in ds.launchtime.values]
      
      # how far along the radius = DAY NUM 
      le = LabelEncoder()
      days = le.fit_transform(ds["launchtime.dayofyear"].values)
      
      
      # Use the polar projection plot from matplotlib
      ax = plt.subplot(projection='polar')
      ax.set_theta_direction(-1)
      ax.set_theta_zero_location("N")
      ax.scatter(angles, days, marker="o")
      ax.set_xticklabels(['00:00', '03:00', '06:00', '09:00', '12:00', '15:00', '18:00', '21:00',])
      ax.set_yticklabels([])
      ax.set_title("Sampling Schedule [UTC]")
      plt.show()
      

      输出看起来像这样:

      【讨论】:

        猜你喜欢
        • 2017-05-18
        • 1970-01-01
        • 2014-04-29
        • 2016-08-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-12
        相关资源
        最近更新 更多