如何创建堆积条形图?

How to Create A Stacked Bar Chart?

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

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

Table 1:

_id Application usage_time
0 Google Chrome 245.283942928347
0 Finder 123.384234239734
0 PyCharm 100.484829432934
1 PyCharm 1646.46116232872
1 SQLiteStudio 160.25696277618408
1 Google Chrome 1756.8145654201508
1 Microsoft Teams 150.2583293914795

Table 2:

Date daily_id
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.3842342397347,0],[= 43 = 43 =]: =15=]

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

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()

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

如您所见,第一个条有重叠,第二个堆叠条中缺少 Google Chrome 条,Finder 条非常小,即使它不是与其他数据相比很小。

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

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 循环,但我建议您将字典放入 pandas 数据框并使用 pandas DataFrame.plot.bar(stacked=True)。为堆叠条形图设置 'bottom' 或 'left' 需要很多麻烦。

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)