63758


[![時系列のデータ](https://i.stack.imgur.com/nyfBu.png)](https://i.stack.imgur.com/nyfBu.png)

このような時系列のデータがあります。  
このデータと同じ形式のデータからx軸を日付、y軸を時間として一つのグラフに表す作業を行っていますが、そのため、日付を2020年03月01日に固定し時と分をy座標として表せ、X軸のlabelを元の時間に変換し下図のように求めたいグラフを作ることができました。  
[![画像の説明をここに入力](https://i.stack.imgur.com/EnEEL.png)](https://i.stack.imgur.com/EnEEL.png)  
しかし、時間の範囲が上の表が示すように23時～25時の時、  
実際の時間の差は1時間弱なものの、グラフが表す範囲が広すぎるのでグラフを綺麗に描くことが出来ず下図のような状況になっております。[![画像の説明をここに入力](https://i.stack.imgur.com/RHuQG.png)](https://i.stack.imgur.com/RHuQG.png)  
グラフを綺麗に描くためdataframeからmax（最大値）とmin（最小値）の時間差が23時間以上の時dataframeから時間が23時間以上差がある時間に24時を足す、グラフの範囲を23時から25時までに設定しグラフを描きたいです。私が書いたコードも添付します。  
作業が上手く進めるようどうぞよろしくお願い致します。

[code]

    plt.clf()
    #####read files###########################
    df = pd.read_excel('files',parse_dates=[0])
    #####to make xlabel###########################
    x = range(len(df))
    xla =df['UTC'].dt.strftime('%Y-%m-%d')
    #####set the days same date ###########################
    y = df['UTC'].apply(lambda x: x.replace(year=2020, month=3, day=1))
    ax = plt.subplot()
    ax.plot(x, y ,marker='s', color='k')
    
    ax.yaxis.set_major_locator(md.MinuteLocator(interval=5))
    ax.yaxis.set_major_formatter(mdates.DateFormatter("%H:%M"))
    plt.xticks(x,xla)
    plt.xticks(rotation=90)
    ax.xaxis.grid(True)
    ax.yaxis.grid(True)
    plt.title('time_of_waypoint', fontsize=10)
    plt.xlabel('day')
    plt.ylabel('time')
    
[/code]


CC BY-SA 4.0
2020-03-10T11:22:57.993
63738
2020-03-11T23:54:36.867
2020-03-10T12:28:12.477
19110
37823
question
python python3 matplotlib
23時～25時の時間の値をy軸としてplotしたいです。


単純に

  * 日時から1時間を引いた値で日付を求める（X軸）
  * 上記の日付の０時を基準にデータの日時との差を求める（Y軸）

をプロットすると良いのではないでしょうか

下記サンプルでは Y軸のデータを秒(total_seconds)で求めておき、Y軸の目盛りを"時:分"
表記に変換(FuncFormatterを使用)しております。

[code]

    import math
    import pandas as pd
    import matplotlib.pyplot as plt
    import matplotlib.dates as mdates
    from matplotlib.ticker import FuncFormatter
    
    df = pd.DataFrame({'UTC':['2017/3/1 23:42',
                              '2017/3/3 23:57',
                              '2017/3/5 23:53',
                              '2017/3/8 00:03',
                              '2017/3/9 23:54',
                              '2017/3/11 23:44',
                              '2017/3/13 23:51',
                              '2017/3/16 00:00',
                              '2017/3/17 23:39',
                              '2017/3/19 23:51',
                              '2017/3/21 23:55',
                              '2017/3/24 00:05',
                              '2017/3/25 23:44',
                              '2017/3/27 23:38',
                              '2017/3/29 23:38']})
    df['UTC'] = pd.to_datetime(df['UTC'])
    
    # 日時から1時間を引いた値で日付を求める（X軸）
    df['date'] = (df['UTC'] - pd.Timedelta('01:00:00')).dt.normalize()
    # 上記の日付を基準に時間を秒で求める（Y軸）
    df['time'] = (df['UTC'] - df['date']).dt.total_seconds()
    
    ax = plt.subplot()
    df.plot(x='date', y='time', ax=ax)
    
    def seconds2time(seconds, pos):
        h = math.floor(seconds / 3600)
        m = math.floor(seconds / 60 % 60)
        return f'{h:02d}:{m:02d}'
    
    ax.yaxis.set_major_formatter(FuncFormatter(seconds2time))
    ax.yaxis.set_ticks(range(23*60*60, 25*60*60+1, 30*60))
    ax.set_ylim(23*60*60, 25*60*60)
    ax.grid(True)
    plt.show()
    
[/code]

[![画像の説明をここに入力](https://i.stack.imgur.com/9pSio.png)](https://i.stack.imgur.com/9pSio.png)

* * *

**【補足】**

Y軸の求め方がわかりにくいとのことでしたので、`”2017/3/24 00:05”` の場合を例に具体的い説明します

**X軸：**  
`"2017/3/4 00:05"` の１時間前は `"2017/3/3 23:05"` なので求められる日付は `"2017/3/3"` となる

実際のコードを見ると

[code]

    df['date'] = (df['UTC'] - pd.Timedelta('01:00:00')).dt.normalize()
    
[/code]

となっており、`dt.normalize()` によって時刻が午前0時に丸められるので

`"2017/3/3 00:00"`

が **'date'列** のデータとして保存されることになります

**Y軸：**  
本来であればY軸には時刻を表示したいのですが、残念ながら pythonのdatetime型は 24時以降 を扱えません。  
そこで上記のX軸のデータ `"2017/3/3 00:00"` から `"2017/3/4 00:05"` の時差を求めて表示します。  
（`"2017/3/3 00:00"` から `"2017/3/4 00:05"` までどのくらいの時間が経過しているかと考えてもOK）  
これにより、Y軸は時刻（datetime型）ではなく経過時間つまり timedelta型のデータとなるので、24時間以上も扱うことができるようになります。

具体的に今回の例で求められる結果は `"2017/3/3 00:00"` と `"2017/3/4 00:05"` の時差 `"24:05"` となります。

と、ここまでが基本的な考え方です。

上記の方法でそのまま実装しても問題ないのですが、今回のサンプルではY軸を
timedelta型で持つとグラフを書く際の目盛りの調整が面倒そうだったので（失礼）さらに簡略化するために

[code]

    df['time'] = (df['UTC'] - df['date']).dt.total_seconds()
    
[/code]

として、Y軸は経過時刻を秒単位の int型 のデータに変換しております。  
`.dt.total_soconds()` により、"24:05" が秒に変換されて  
`86700`  
という数値が **'time'列** のデータとして保存されることになります。

ただ、このまま上記のデータをプロットするとY軸の値は秒単位となってしまいますので、目盛りの表示を `seconds2time()`関数にて

[code]

    def seconds2time(seconds, pos):
        h = math.floor(seconds / 3600)
        m = math.floor(seconds / 60 % 60)
        return f'{h:02d}:{m:02d}'
    
[/code]

のように "時：分" 表示に変換して目盛りを書くといういことを行っております。


CC BY-SA 4.0
2020-03-11T07:26:46.713
63758
2020-03-11T23:54:36.867
2020-03-11T23:54:36.867
24801
24801
63738
answer