【问题标题】:How to Create A Stacked Bar Chart?如何创建堆积条形图?
【发布时间】:2021-09-28 19:27:21
【问题描述】:

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

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

表 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

表 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],'Pycharm':[100.4848294329348,1646.46129348,1646.46116232872],'其他':[0,310.5152921676636]}

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

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 栏非常小,尽管与其他数据相比并没有那么小。

关于如何修复此堆积条形图的任何想法?还希望对如何改进数据处理提出建议

【问题讨论】:

    标签: python python-3.x matplotlib bar-chart stacked-chart


    【解决方案1】:

    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)
    

    【讨论】:

    • 现在可以使用了,谢谢!不过,您对如何提高数据处理性能有什么建议吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-27
    • 1970-01-01
    • 1970-01-01
    • 2017-01-23
    相关资源
    最近更新 更多