热图中的日期轴seaborn

问题描述 投票:0回答:5

一点信息:我对编程非常陌生,这是我第一个脚本的一小部分。此特定部分的目标是显示 Seaborn 热图,其中 y 轴为垂直深度,x 轴为时间,科学测量的强度作为热函数。

如果这个问题在其他地方得到了回答,我很抱歉,但我的搜索能力一定让我失望了。

sns.set()
nametag = 'Well_4_all_depths_capf'
Dp = D[D.well == 'well4']
print(Dp.date)


heat = Dp.pivot("depth",  "date", "capf")
### depth, date and capf are all columns of a pandas dataframe 

plt.title(nametag)

sns.heatmap(heat,  linewidths=.25)

plt.savefig('%s%s.png' % (pathheatcapf, nametag), dpi = 600)

这是从“ print(Dp.date) ”打印的内容 所以我很确定数据帧的格式是我想要的格式,特别是年、日、月。

0    2016-08-09
1    2016-08-09
2    2016-08-09
3    2016-08-09
4    2016-08-09
5    2016-08-09
6    2016-08-09
         ...    

但是,当我运行它时,日期轴总是打印出我不想要的空白时间(00:00 等)。 有没有办法从日期轴上删除这些?

问题是在上面的单元格中我使用此函数扫描文件名并创建带有日期的列吗?使用日期时间而不是日期函数是错误的吗?

D['date']=pd.to_datetime(['%s-%s-%s' %(f[0:4],f[4:6],f[6:8]) for f in             
D['filename']])

python datetime matplotlib seaborn heatmap
5个回答
12
投票

您必须对数据框的日期系列使用 strftime 函数才能正确绘制 xtick 标签:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta
import random

dates = [datetime.today() - timedelta(days=x * random.getrandbits(1)) for x in xrange(25)]
df = pd.DataFrame({'depth': [0.1,0.05, 0.01, 0.005, 0.001, 0.1, 0.05, 0.01, 0.005, 0.001, 0.1, 0.05, 0.01, 0.005, 0.001, 0.1, 0.05, 0.01, 0.005, 0.001, 0.1, 0.05, 0.01, 0.005, 0.001],\
 'date': dates,\
 'value': [-4.1808639999999997, -9.1753490000000006, -11.408113999999999, -10.50245, -8.0274750000000008, -0.72260200000000008, -6.9963940000000004, -10.536339999999999, -9.5440649999999998, -7.1964070000000007, -0.39225599999999999, -6.6216390000000001, -9.5518009999999993, -9.2924690000000005, -6.7605589999999998, -0.65214700000000003, -6.8852289999999989, -9.4557760000000002, -8.9364629999999998, -6.4736289999999999, -0.96481800000000006, -6.051482, -9.7846860000000007, -8.5710630000000005, -6.1461209999999999]})
pivot = df.pivot(index='depth', columns='date', values='value')

sns.set()
ax = sns.heatmap(pivot)
ax.set_xticklabels(df['date'].dt.strftime('%d-%m-%Y'))
plt.xticks(rotation=-90)

plt.show()


4
投票

标准热图日期时间标签示例

import pandas as pd
import seaborn as sns

dates = pd.date_range('2019-01-01', '2020-12-01')

df = pd.DataFrame(np.random.randint(0, 100, size=(len(dates), 4)), index=dates)

sns.heatmap(df)

我们可以创建一些辅助类/函数来获得一些更好看的标签和位置。

AxTransformer
允许从数据坐标到刻度位置的转换,
set_date_ticks
允许将自定义日期范围应用于绘图。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from collections.abc import Iterable
from sklearn import linear_model

class AxTransformer:
    def __init__(self, datetime_vals=False):
        self.datetime_vals = datetime_vals
        self.lr = linear_model.LinearRegression()
        
        return
    
    def process_tick_vals(self, tick_vals):
        if not isinstance(tick_vals, Iterable) or isinstance(tick_vals, str):
            tick_vals = [tick_vals]
            
        if self.datetime_vals == True:
            tick_vals = pd.to_datetime(tick_vals).astype(int).values
            
        tick_vals = np.array(tick_vals)
            
        return tick_vals
    
    def fit(self, ax, axis='x'):
        axis = getattr(ax, f'get_{axis}axis')()
        
        tick_locs = axis.get_ticklocs()
        tick_vals = self.process_tick_vals([label._text for label in axis.get_ticklabels()])
        
        self.lr.fit(tick_vals.reshape(-1, 1), tick_locs)
        
        return
    
    def transform(self, tick_vals):        
        tick_vals = self.process_tick_vals(tick_vals)
        tick_locs = self.lr.predict(np.array(tick_vals).reshape(-1, 1))
        
        return tick_locs
    
def set_date_ticks(ax, start_date, end_date, axis='y', date_format='%Y-%m-%d', **date_range_kwargs):
    dt_rng = pd.date_range(start_date, end_date, **date_range_kwargs)

    ax_transformer = AxTransformer(datetime_vals=True)
    ax_transformer.fit(ax, axis=axis)
    
    getattr(ax, f'set_{axis}ticks')(ax_transformer.transform(dt_rng))
    getattr(ax, f'set_{axis}ticklabels')(dt_rng.strftime(date_format))

    ax.tick_params(axis=axis, which='both', bottom=True, top=False, labelbottom=True)
    
    return ax

这些为我们提供了很大的灵活性,例如

fig, ax = plt.subplots(dpi=150)

sns.heatmap(df, ax=ax)

set_date_ticks(ax, '2019-01-01', '2020-12-01', freq='3MS')

或者如果你真的想变得奇怪,你可以做类似的事情

fig, ax = plt.subplots(dpi=150)

sns.heatmap(df, ax=ax)

set_date_ticks(ax, '2019-06-01', '2020-06-01', freq='2MS', date_format='%b `%y')

对于您的具体示例,您必须将

axis='x'
传递给
set_date_ticks


1
投票
  • 首先,
    'date'
    列必须转换为带有
    datetime dtype
    pandas.to_datetime
  • 如果所需的结果是只有日期(没有时间),那么最简单的解决方案是使用
    .dt
    访问器
    提取
    .date
    组件。或者,使用
    dt.strftime
    设置特定的字符串格式。
    • strftime()
      strptime()
      格式代码
    • df.date.dt.strftime('%H:%M')
      会将小时和分钟提取到字符串中,如
      '14:29'
    • 在下面的示例中,提取的日期被分配给同一列,但该值也可以分配为新列。
  • 如果每个
  • pandas.DataFrame.pivot_table
     的列中有多个值,则使用 
    index
    来聚合函数,如果只有一个值,则应使用
    pandas.DataFrame.pivot
    • 这比
      .groupby
      更好,因为数据框的形状正确,可以轻松绘制。
  • 已在
    python 3.8.11
    pandas 1.3.2
    matplotlib 3.4.3
    seaborn 0.11.2
  • 进行测试
import pandas as pd
import numpy as np
import seaborn as sns

# create sample data
dates = [f'2016-08-{d}T00:00:00.000000000' for d in range(9, 26, 2)] + ['2016-09-09T00:00:00.000000000']
depths = np.arange(1.25, 5.80, 0.25)
np.random.seed(365)
p1 = np.random.dirichlet(np.ones(10), size=1)[0]  # random probabilities for random.choice
p2 = np.random.dirichlet(np.ones(19), size=1)[0]  # random probabilities for random.choice
data = {'date': np.random.choice(dates, size=1000, p=p1), 'depth': np.random.choice(depths, size=1000, p=p2), 'capf': np.random.normal(0.3, 0.05, size=1000)}
df = pd.DataFrame(data)

# display(df.head())
                            date  depth      capf
0  2016-08-19T00:00:00.000000000   4.75  0.339233
1  2016-08-19T00:00:00.000000000   3.00  0.370395
2  2016-08-21T00:00:00.000000000   5.75  0.332895
3  2016-08-23T00:00:00.000000000   1.75  0.237543
4  2016-08-23T00:00:00.000000000   5.75  0.272067

# make sure the date column is converted to a datetime dtype
df.date = pd.to_datetime(df.date)

# extract only the date component of the date column
df.date = df.date.dt.date

# reshape the data for heatmap; if there's no need to aggregate a function, then use .pivot(...)
dfp = df.pivot_table(index='depth', columns='date', values='capf', aggfunc='mean')

# display(dfp.head())
date   2016-08-09  2016-08-11  2016-08-13  2016-08-15  2016-08-17  2016-08-19  2016-08-21  2016-08-23  2016-08-25  2016-09-09
depth                                                                                                                        
1.50     0.334661         NaN         NaN    0.302670    0.314186    0.325257    0.313645    0.263135         NaN         NaN
1.75     0.305488    0.303005    0.410124    0.299095    0.313899    0.280732    0.275758    0.260641         NaN    0.318099
2.00     0.322312    0.274105         NaN    0.319606    0.268984    0.368449    0.311517    0.309923         NaN    0.306162
2.25     0.289959    0.315081         NaN    0.302202    0.306286    0.339809    0.292546    0.314225    0.263875         NaN
2.50     0.314227    0.296968         NaN    0.312705    0.333797    0.299556    0.327187    0.326958         NaN         NaN

# plot
sns.heatmap(dfp, cmap='GnBu')


0
投票

我发现对我来说最简单的事情就是获取刻度线,重新格式化它们,然后将它们放回去。这避免了计算刻度的数量和位置的需要,因为 matplotlib 已经做到了这一点。您只需替换文本即可。 (见下面的注释)

def format_date_ticks(old_ticks:list[plt.Text])->list[str]:
    text = [l.get_text() for l in old_ticks] # plt.Text to str
    return pd.to_datetime(text).date # str to datetime, then format as desired

# ...

new_ticks = format_date_ticks(ax.get_xticklabels()) # get and transform old ticks
ax.set_xticklabels(new_ticks) # replace the old with new

注意:根据对另一个答案的评论:

现在给出 ValueError: The number of FixedLocatorlocations (13),通常来自对 set_ticks 的调用,与刻度标签的数量 (25) 不匹配。,所以不确定它是否完全正确...

“请注意,设置刻度位置 (set_xticks) 和刻度标签 (set_xticklabels) 很重要,否则它们将不同步。”来自 matplotlib 文档


-3
投票

我有类似的问题,但日期是索引。我刚刚在绘图之前将日期转换为字符串(pandas 1.0),它对我有用。

heat['date'] = heat.date.astype('string')
© www.soinside.com 2019 - 2024. All rights reserved.