I have data over time (consumption of gas, water, electricity) that I want to plot - from the past to today. But newer data is more important than older data. This is what I want as xaxis:
- Data from today till back one week - daily
- Data from one week till three weeks back - weekly
- Data older, monthly
The xticks would then would - roughly - look like this: 2022-05 2022-06 2022-07 2022-07-25 2022-08-01 2022-08-08 2022-08-09 2022-08-10 2022-08-11
AND would be equally spaced!. This would give me a good overview what happened yesterday, last week, and "in the entire past". However, I pretty much got stuck with the forward and reverse functions and the return values.
Does anyone have a example? I got this far only:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
def forward(x):
return x
def reverse(x):
return x
rng = pd.date_range(end='2022-08-14', periods=180, freq='D')
df = pd.DataFrame({'Date': rng, 'Val': np.random.randn(len(rng)) })
df.set_index("Date",inplace=True)
ax=df.plot()
plt.gca().set_xscale('function', functions=(forward, reverse))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.xticks(rotation = 30)
plt.show()
CodePudding user response:
import numpy as np
idx = np.full(df.shape[0], False)
idx[-7:] = True
idx[-7:-3*7:-7] = True
idx[-3*7::-30] = True
df2 = df[idx]
print(df)
Date | Val |
---|---|
2022-02-25 00:00:00 | 1.1248252961101586 |
2022-03-27 00:00:00 | 0.04203875889903905 |
2022-04-26 00:00:00 | 0.5896163070537574 |
2022-05-26 00:00:00 | -1.8329001623876962 |
2022-06-25 00:00:00 | -1.0173128750157898 |
2022-07-25 00:00:00 | -0.6708114840150123 |
2022-08-01 00:00:00 | 0.2657251487401089 |
2022-08-08 00:00:00 | -0.6617901994374795 |
2022-08-09 00:00:00 | 0.17380650386233432 |
2022-08-10 00:00:00 | -0.13383053082508994 |
2022-08-11 00:00:00 | -1.1532937501757903 |
2022-08-12 00:00:00 | -0.5940972756876102 |
2022-08-13 00:00:00 | -0.9253845051895224 |
2022-08-14 00:00:00 | -0.5568183429382052 |
import matplotlib.pyplot as plt
plt.plot(df2.index.astype(str), df2.Val);
plt.xticks(rotation=45);
CodePudding user response:
Creating a custom scale for matplotlib
Two transformations are needed:
- Compress each of the previous 3 weeks into the length of a day.
- Compress each months, except the latest, into the length of a day.
Before any activity, let's simplify the data. I do it to avoid any hassle with datetime
:
x = np.arange(120)
y = x
The main line is simple - if a date is earlier some endpoint then push it forward at the given scale. Since we compress data twice in the same manner, let's make a factory:
def compress_factory(endpoint, scale):
return lambda x: scale * x (1-scale) * endpoint
Illustration for the formula above
Now, prepare two pair of supportive functions, for the week and month:
w_ago = x[-1] - 7
w_forward = compress_factory(w_ago, 1/7)
w_backward = compress_factory(w_ago, 7)
m_ago = x[-1] - 30
leap = w_forward(m_ago) - m_ago
m_forward = lambda x: compress_factory(m_ago, 1/30)(x) leap
m_backward = lambda x: compress_factory(m_ago, 30)(x - leap)
We apply them in two consequent np.where
to separate scaling of the corrent week, previous 3 weeks and all the other dates:
def forward(x):
return np.where(
x > w_ago,
x,
np.where(
x > m_ago,
w_forward(x),
m_forward(x)
)
)
def backward(x):
return np.where(
x > w_ago,
x,
np.where(
x > w_forward(m_ago),
w_backward(x),
m_backward(x)
)
)
Take into account that in backward we compare data to transformed month endpoint. The first comparison stay the same because we don't scale the latest week.
Two sanity tests:
# 1. Does backward(forward(x)) equal x?
assert np.isclose(backward(forward(x)), x).all()
# 2. Is forward monotonic?
assert (((_:=forward(x))[1:] - _[:-1]) >= 0).all()
Let's see how it looks on simplified data:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(1, 91)
y = x
def compress_factory(endpoint, size):
return lambda x: size * x (1-size) * endpoint
w_size = 7
w_ago = x[-1] - w_size
w_forward = compress_factory(w_ago, 1/w_size)
w_backward = compress_factory(w_ago, w_size)
m_size = 30
m_ago = x[-1] - m_size
leap = w_forward(m_ago) - m_ago
m_forward = lambda x: compress_factory(m_ago, 1/m_size)(x) leap
m_backward = lambda x: compress_factory(m_ago, m_size)(x - leap)
def forward(x):
return np.where(
x > w_ago,
x,
np.where(
x > m_ago,
w_forward(x),
m_forward(x)
)
)
def backward(x):
return np.where(
x > w_ago,
x,
np.where(
x > w_forward(m_ago),
w_backward(x),
m_backward(x)
)
)
assert np.isclose(backward(forward(x)), x).all()
assert (((_:=forward(x))[1:] - _[:-1]) >= 0).all()
fig, ax = plt.subplots(figsize=(16,5))
ax.plot(x, y)
ax.grid()
ax.set_xscale('function', functions=(forward, backward))
ax.set_xbound(min(x), max(x))
ax.set_ybound(min(y), max(y))