问题
I am working on my first project and I am using Pygal to visualize some data from a database.
I am using the latest version of Python (3.6.5), Flask, Pygal and my IDE is Pycharm
The project is a simple budget application in which one enters the planned budget for a monthly expenditure and then the actual amounts for that expense/item (e.g. monthly car expenses, like gas).
I use Pygal to show 2 bar charts. The first one (which works as intended) shows a total planned amounts vs total actual amounts:
The second chart shows the planned vs actual per expense/item (e.g. monthly car expenses, like gas)
The issue I am facing is that the chart mixes up the labels and amounts. They show up in the chart based on the order they are entered, not on the item type.
For example:
In the above image, the are 3 items: Masina (car), Salariu (salary) and Economii (savings).
The amount represented by the blue column (the actual amount) should show up under the label Economii, not under Masina and it should not matter that I have entered it as the first actual in the database.
Also, adding more actual amounts for the same expenses item (Economii in our case) in the database simply adds more columns and it does not sum it up on the same column:
This is the database query function I am using:
def GraphData():
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
db_path = os.path.join(BASE_DIR, 'budget_db.db')
with sqlite3.connect(db_path) as db:
c = db.cursor()
d = db.cursor()
c.execute('SELECT title, category, name, planned_amount_month FROM Post')
d.execute('SELECT title_actual, category_actual, actual_amount_name, actual_amount FROM ActualPost')
data_planned = c.fetchall()
data_actual = d.fetchall()
return data_planned, data_actual
Below is the route I have created for both of the charts. The labels are pulled from the title_planned
list and I have a feeling that the issue I am facing is because I am creating lists and I am appending them.
I think I should create dictionaries, but I have not idea how without messing everything else up:
@posts.route("/home")
def graphing():
data_planned, data_actual = GraphData()
title_planned = []
value_planned = []
title_actual = []
value_actual = []
for planned_row in data_planned:
title_planned.append(planned_row[2])
value_planned.append(planned_row[3])
for actual_row in data_actual:
title_actual.append(actual_row[2])
value_actual.append(actual_row[3])
graph = pygal.Bar(title=u'Total Planned Values vs Total Actual Values')
graph.add('Planned', [{'value': sum(value_planned), 'label': 'Total for Planned Budget:'}])
graph.add('Actual', [{'value': sum(value_actual), 'label': 'Total for Actual Amounts:'}])
graph_data = graph.render_data_uri()
graph_all = pygal.Bar(title=u'Planned Budget per item vs Actual Amounts per item')
graph_all.x_labels = title_planned
graph_all.add('Planned', value_planned)
graph_all.add('Actual', value_actual)
graph_all_data = graph_all.render_data_uri()
return render_template('home.html', graph_data=graph_data, graph_all_data=graph_all_data)
Edit:
I have been trying to do it using 2 dictionaries in the route with the expense item as dict key (title_planned_sum
/ title_actual_sum
) and the amount as dict value (value_planned_sum
/ value_actual_sum
):
tv_planned = dict(zip(title_planned, value_planned))
tv_planned_sum = {title_planned_sum: sum(value_planned_sum) for title_planned_sum, value_planned_sum in tv_planned.items()}
tv_actual = dict(zip(title_actual, value_actual))
tv_actual_sum = {title_actual_sum: sum(value_actual_sum) for title_actual_sum, value_actual_sum in tv_actual.items()}
Here is the full route:
@posts.route("/home")
def graphing():
data_planned, data_actual = GraphData()
title_planned = []
value_planned = []
title_actual = []
value_actual = []
for planned_row in data_planned:
title_planned.append(planned_row[2])
value_planned.append(planned_row[3])
for actual_row in data_actual:
title_actual.append(actual_row[2])
value_actual.append(actual_row[3])
tv_planned = dict(zip(title_planned, value_planned))
tv_planned_sum = {title_planned_sum: sum(value_planned_sum) for title_planned_sum, value_planned_sum in tv_planned.items()}
tv_actual = dict(zip(title_actual, value_actual))
tv_actual_sum = {title_actual_sum: sum(value_actual_sum) for title_actual_sum, value_actual_sum in tv_actual.items()}
graph = pygal.Bar(title=u'Total Planned Values vs Total Actual Values')
graph.add('Planned', [{'value': sum(value_planned), 'label': 'Total for Planned Budget:'}])
graph.add('Actual', [{'value': sum(value_actual), 'label': 'Total for Actual Amounts:'}])
graph_data = graph.render_data_uri()
graph_all = pygal.Bar(title=u'Planned Budget per item vs Actual Amounts per item')
graph_all.x_labels = title_planned
graph_all.add('Planned', tv_planned_sum)
graph_all.add('Actual', tv_actual_sum)
graph_all_data = graph_all.render_data_uri()
return render_template('home.html', graph_data=graph_data, graph_all_data=graph_all_data)
But of course, now I am getting this debug error:
TypeError: 'float' object is not iterable
And this is because, in the 2 dictionaries I am trying to work with, I am trying to sum the values for each key that receives multiple values with sum(value_planned_sum)
.
回答1:
I got it working!
In the end I realized I was over-complicating my question, so I pulled out a pen and paper and started doing some pseudo-code to see where was the first step in my code where I could comfortably calculate the floats sum that was causing me head-aches.
Step 1: In my route, I could create a list containing nested tuples (with 2 elements each: str and float)
Step 2: Now, if some of the tuples had the same elements on index [0], how do I sum the float elements on index [1]?
So, I asked the this question on reddit.com/r/learnpython and the user diesch gave me an idea that I could use successfully: to import the itertools
package and from it, to use groupby()
.
Here is how my route code looks now:
@posts.route("/home")
def graphing():
data_planned, data_actual = GraphData()
title_planned = []
value_planned = []
title_actual = []
value_actual = []
planned = []
actual = []
for planned_row in data_planned:
title_planned.append(planned_row[2])
value_planned.append(planned_row[3])
planned_list = zip(title_planned, value_planned)
for key, group in itertools.groupby(sorted(planned_list), lambda x: x[0]):
asum = 0
for i in group:
asum += i[1]
planned.append((key, asum))
planned_dict = dict(planned)
for actual_row in data_actual:
title_actual.append(actual_row[2])
value_actual.append(actual_row[3])
actual_list = zip(title_actual, value_actual)
for key, group in itertools.groupby(sorted(actual_list), lambda x: x[0]):
asum = 0
for i in group:
asum += i[1]
actual.append((key, asum))
actual_dict = dict(actual)
graph = pygal.Bar(title=u'Total Planned Values vs Total Actual Values')
graph.add('Planned', [{'value': sum(value_planned), 'label': 'Total for Planned Budget:'}])
graph.add('Actual', [{'value': sum(value_actual), 'label': 'Total for Actual Amounts:'}])
graph_data = graph.render_data_uri()
graph_all = pygal.Bar(title=u'Planned Budget per item vs Actual Amounts per item')
graph_all.x_labels = title_planned
graph_all.add('Planned', planned_dict)
graph_all.add('Actual', actual_dict)
graph_all_data = graph_all.render_data_uri()
return render_template('home.html', graph_data=graph_data, graph_all_data=graph_all_data)
来源:https://stackoverflow.com/questions/50748569/python-pygal-chart-pulling-data-from-database-not-matching-values-to-labels