首页 > 解决方案 > 如何创建堆积条形图?

问题描述

我正在编写一个程序来监控和记录前台应用程序的使用时间并将它们保存在 SQL 数据库中。然后,我想检索前几天的数据并将其全部编译成堆叠条形图。在这里,x 轴将记录使用记录的不同日期,每个条形图中的各种堆栈将代表使用的每个应用程序。

在我的程序中,我创建了 2 个表,一个用于记录每天的应用程序使用情况(每天的数据具有不同的主键 ID),另一个用于记录每天的主键。

表格1:

_ID 应用 使用时间
0 谷歌浏览器 245.283942928347
0 发现者 123.384234239734
0 PyCharm 100.484829432934
1 PyCharm 1646.46116232872
1 SQLiteStudio 160.25696277618408
1 谷歌浏览器 1756.8145654201508
1 微软团队 150.2583293914795

表 2:

日期 每日编号
2021-07-18 07:25:25.376734 0
2021-07-18 07:27:57.419574 1

在我的堆积条形图程序中,我提出了以下代码来优化数据以放入堆积条形图中:

conn = sqlite3.connect('daily_usage_monitor.sqlite', detect_types=sqlite3.PARSE_DECLTYPES)

all_app_data = conn.execute('SELECT all_usage_information.date, monitor.application, monitor.usage_time '
                            'FROM all_usage_information '
                            'INNER JOIN monitor ON all_usage_information.daily_id = monitor._id '
                            'ORDER BY all_usage_information.date, monitor.usage_time ASC').fetchall()

for date, app, usage_time in all_app_data:
    print(f'{date} - {app}: {usage_time}')
conn.close()

daily_data = {}

# Create nested dictionary - key = each date, value = dictionary of different apps & their time usage durations
for date, app, time in all_app_data:
    conditions = [date not in daily_data, app != 'loginwindow']
    if all(conditions):
        daily_data[date] = {app: time}
    elif not conditions[0] and conditions[1]:
        daily_data[date].update({app: time})

print(daily_data)   # TODO: REMOVE AFTER TESTING

total_time = 0
# Club any applications that account for <5% of total time into 1 category called 'Other'
for date, app_usages in daily_data.items():
    total_time = sum(time for app, time in app_usages.items())

    refined_data = {}
    for key, value in app_usages.items():
        if value/total_time < 0.05:
            refined_data['Others'] = refined_data.setdefault('Others', 0) + value
        else:
            refined_data[key] = value
    daily_data[date] = refined_data

print(daily_data)   # TODO: REMOVE AFTER TESTING

# Add key:value pairs initializing apps to 0 which are either used in past and never used again
# or used in future but not in past
used_apps = set()
counter = 0
for date, app_usages in reversed(daily_data.items()):
    for app, time in app_usages.items():
        used_apps.add(app)
    counter += 1
    if counter != 1:
        for used_app in used_apps:
            if used_app not in app_usages.keys():
                app_usages[used_app] = 0

used_apps = set()
counter = 0
for date, app_usages in daily_data.items():
    for app, time in app_usages.items():
        used_apps.add(app)
    counter += 1
    if counter != 1:
        for used_app in used_apps:
            if used_app not in app_usages.keys():
                app_usages[used_app] = 0
print(daily_data)   # TODO: REMOVE AFTER TESTING

# Takes the nested dictionary and breaks it into a labels list and a dictionary with apps & time usages for each day
# Sorts data so it can be used to create composite bar chart
final_data = {}
labels = []
for date, app_usages in daily_data.items():
    labels.append(date.strftime('%d/%m/%Y'))
    for app, time in app_usages.items():
        # time = datetime.timedelta(seconds=time)   # TODO: CHECK WHAT TO DO
        if app not in final_data:
            final_data[app] = [time]
        else:
            final_data[app].append(time)
print(final_data)
final_data = dict(sorted(final_data.items(), key=lambda x: x[1], reverse=True))

print(final_data)   # TODO: REMOVE AFTER TESTING

此处理为此提供了此输出:{'Google Chrome':[245.283942928347,1756.8145654201508],'Finder':[123.3842342342342342397347,0]

然后,为了创建堆积条形图,这是我编写的代码:

width = 0.5
counter = 0
fig, ax = plt.subplots()
for key, value in final_data.items():
    if counter == 0:
        ax.bar(labels, value, width=width, label=key)
    else:
        ax.bar(labels, value, width=width, bottom=bottom, label=key)
    bottom = value
    counter += 1
ax.set_ylabel('Time usage on applications')
ax.set_xlabel('Dates (DD-MM-YYYY)')
ax.set_title('Time Usage Trend')
ax.legend()
plt.show()

但是,这是我得到的输出:

在此处输入图像描述

如您所见,第 1 个条有重叠,第 2 个堆叠条缺少 Google Chrome 条,Finder 条非常小,尽管与其他数据相比并没有那么小。

关于如何修复这个堆积条形图的任何想法?还将感谢有关如何改进数据处理的建议

标签: pythonpython-3.xmatplotlibbar-chartstacked-chart

解决方案


matplotlib 堆积条形图疑难解答

看起来你的问题是 for 循环。您正在遍历键和值,并在每次迭代时绘制值。

for key, value in final_data.items():
    print(key, value)

Google Chrome [245.283942928347, 1756.8145654201508]
Finder [123.3842342397347, 0]
PyCharm [100.4848294329348, 1646.46116232872]
Others [0, 310.5152921676636]

您打算做的是每次迭代绘制每个 LABEL。您可以更改您的 for 循环,但我建议您将 dict 放入 pandas 数据框并使用pandas DataFrame.plot.bar(stacked=True)。为堆积条形图设置“底部”或“左侧”需要很多麻烦。

final_data = pd.DataFrame({
    'Google Chrome': [245.283942928347, 1756.8145654201508], 
    'Finder': [123.3842342397347, 0], 
    'PyCharm': [100.4848294329348, 1646.46116232872], 
    'Others': [0, 310.5152921676636]}
)

final_data.plot.bar(stacked=True)

在此处输入图像描述


推荐阅读