J Spratt
J Spratt

Reputation: 2012

matplotlib: bar chart with datetime x axis causing uncontrollable bar width

I've been trying to find a solution to this for quite some time. I am using pandas and matplotlib to write a plotting library. I am currently in the process of correcting an error in design which was using strings instead of timestamps. One reason this is an error because it prevents any easy way of filling missing timestamps.

The goal is to create a stacked bar chart that looks similar to the following chart but with only a portion of the timestamp. (HH:MM, YYYY-MM-DD, YYYY-MM, etc.) This bar chart is produced with a regular pandas Index.

df = pd.read_csv('/path/to/file.csv', index_col='ts')

Decent Chart - not enough rep to embed images yet

After converting my index to a pandas DateTimeIndex the following chart is produced with the same data.

df = pd.read_csv('/path/to/file.csv', index_col='ts', parse_dates=True, infer_datetime_format=True)

Illegible Chart

After some manipulation here is the structure of the data frame being used for plotting:

work_type            CYCLECOUNT  PICK  REPLENISHMENT
ts                                                  
2018-10-25 05:00:00          35     0              5
2018-10-25 06:00:00          95     0              1
2018-10-25 07:00:00         125     0              1
2018-10-25 08:00:00           8     0              0
2018-10-25 09:00:00          19     0              3
2018-10-25 11:00:00           0     0             89
2018-10-25 12:00:00           1     0             59
2018-10-25 13:00:00           1   541            208
2018-10-25 14:00:00           0   516            123
2018-10-25 15:00:00           0   390             95
2018-10-25 16:00:00           2   315            167
2018-10-25 17:00:00           0   784             87
2018-10-25 18:00:00           1   521            145
2018-10-25 19:00:00           1   768            196
2018-10-25 20:00:00           0   769            113
2018-10-25 21:00:00           4   785            139
2018-10-25 22:00:00           0   858             81
2018-10-25 23:00:00           2   568             33
2018-10-26 00:00:00           6   530             60
2018-10-26 01:00:00          13   163             50
2018-10-26 02:00:00          12     4            158
2018-10-26 03:00:00           6     5             92

Here is the code being used to generate the chart:

def create_stacked_bar_chart(
    self, dataframe, focus_total, ax: matplotlib.axes.Axes=None,
    top_bar_lbls=False, mid_bar_lbls=False, mid_bar_as_percent=False
):
        i = 0  # part_map keys
        k = 0  # pivot_df row index
        barwidth = 0.5
        part_map = dict()

        column_names = dataframe.columns.values

        if ax is None:
            fig, ax = self.create_figure()

        for col in column_names:
            if i == 0:
                part_map[i] = ax.bar(
                    dataframe.index,
                    dataframe[col].values,
                    barwidth, zorder=3
                )
            else:
                barstart = 0
                for j in range(i):
                    barstart += dataframe[column_names[j]]
                part_map[i] = ax.bar(
                     dataframe.index, dataframe[col].values,
                     barwidth, bottom=barstart, zorder=3
                )

I have also tried dataframe.index.to_pydatetime() in the ax.bar() function which seems to have no effect. I've tried numerous things but to_pydatetime() seems to be recommended in many places.

Upvotes: 8

Views: 2856

Answers (1)

J Spratt
J Spratt

Reputation: 2012

Update: This solution works for the chart in question in this post but for anyone that might come across this...

===> The unit for bar width on a date x axis is days <===

This was solved by changing the following line from:

barwidth = 0.5

to

barwidth = 0.5 * (1 / dataframe.shape[0])

Where dataframe.shape[0] is equal to the number of rows in the dataframe allowing for dynamic sizing.

Upvotes: 9

Related Questions