Big for inside for. How can I reduce time? - python

I have 2 lists ("IDS" and "Pay"). The len of IDS is 50000 and the len of Pay is 650000
The IDS is a list of IDS, like [1,2,3,4,5,6 ... ] and the PAY list is a list of lists with all payment that the IDS did, like [ [1,50], [1,100], [1,60], [2,50], [2,80], [2,50], ...]
To know how much each ID payed in total, I'm doing a for loop inside another for loop like this one:
for x in IDS:
total = 0
for i in xrange(0,len(Pay)):
if x == Pay[i][0]:
total += Pay[i][1]
print x + str(total)
But it is taking ages to process this!
I've tried to split Pay in 10 pieces, but still taking too long.
Anyone have some idea on how can I improve this operation?
Thank you!

You can use collections.Counter:
>>> from collections import Counter
>>> pay = [ [1,50], [1,100], [1,60], [2,50], [2,80], [2,50]]
>>> c = Counter()
>>> for idx, amt in pay:
c[idx] += amt
...
>>> c
Counter({1: 210, 2: 180})

OK, the fact is you have 2 very long list. Instead of discuss on what library to use, what about a better algorithm?
IDs should naturally contains unique integers (my guess), while Pay is tuples of (id, payment).
Now think about where your list is coming from. There are two possibilities:
Read from file
From some database, like MySQL
If it is option 1, you should do the following instead:
from collections import defaultdict
totals = defaultdict(someObj_factory)
[totals[int(line.split[0])].accumulate(someObj_factory(line.split()[1]))
for line in paymentFile]
First, you don't need the ids as an independent list, because you have them in Pay.
Second, it saves the reading time.
Third, for scripting language, list comprehension saves interpreting time.
Fourth, this is robust, as you can add any object you want, like date or tuples.
If it is option 2, do the counting in your database-.-
Another option is insert these into database, and do the counting there. MySQL etc. are designed for this kind of task. You will be surprise how efficient it is. More info: http://mysql-python.sourceforge.net/

If collections.Counter doesn't work for you - say if you are on a different Python version, - converting your pay list to a dictionary will have the same effect.
totals = {}
for id, amount in pay:
totals[id] = totals.setdefault(id, 0) + amount
Like the date of payment [1,50,2013-09-01] and I have to sum the
values only of dates greater than '2013-01-01'?
Then do this:
import datetime
base_date = datetime.datetime.strptime('2013-01-01', '%Y-%m-%d').date()
totals = {}
for idx, amount, pay_date in pay:
if datetime.datetime.strptime(pay_date, '%Y-%m-%d').date() > base_date:
totals[idx] = totals.setdefault(id, 0) + amount

You only need to iterate over Pay once (rather than 50000 times!). You can drastically speed up your computation by hashing:
totals = dict(map(lambda id: (id,0), IDS))
for L in Pay:
if L[0] in totals:
totals[L[0]] = totals[L[0]] + L[1]
for (id, total) in totals.iteritems():
print "id: %s, total: %d"%(id, total)

Related

How to read from dictionary that has no key in python?

this is a follow up from my previous question where I am working on multiple dictionary and having trouble with them. I have currently have a main dictionary where I store my data and one blank dictionary where I add data into.
Dairy_goods = {1:{'item':'Milk','p':2.47,'g':0.16,'offer':'Yes'},
2:{'item':'Butter','p':4.50,'g':0.32,'offer':'No'},
3:{'item':'Egg','p':3.40,'g':0.24,'offer':'No'}}
shopping_basket={}
Lets say for example I would like to add the item "Milk" into the blank shopping basket, i would do as below.
choose=int(input('1.Item= Milk, Price= $2.47, GST= $0.16, Offer=Yes\n'
'2.item= Butter, Price= $4.50, GST= $0.32, Offer=No\n'
'3.Item= Egg, Price= $4.50, GST= $0.32, Ofer=No\n'
'Enter your option: '))
qnty=int(input('How many do you want?: '))
shopping_basket[Dairy_goods[choose]['item']] = shopping_basket.get(Dairy_goods[choose]['item'], 0) + qnty,Dairy_goods[choose]['p'],Dairy_goods[choose]['g'],Dairy_goods[choose]['offer']
I would get an output as below.
print(shopping_basket)
{'Milk': (2, 2.47, 0.16, 'Yes')}
Now I would like to edit the values inside of this new dictionary, how do I go about doing so? As it has no fix key? I am aware it it is in a tupple and can convert to a list but will still give a error as shown below.
for item in shopping_basket:
item = str(input('key in an item to edit: '))
for item in shopping_basket:
shopping=list(shopping_basket)
qnty = int(input('Key in the quantity of %s you want: ' % item))
shopping[item][0] = qnty # i would get a error here.
print(shopping)
Let me know if more clarifications is needed, thank you in advance.
There are a few things off in your code in terms of data structures. First, a dictionary with incrementing keys {1: ..., 2: ..., ...} is basically the same as a list, so you can just as well use the simpler data structure and select items by indexing. The only thing to note here is that list indices start at 0, so you need to subtract 1 from your user's choice.
Dairy_goods = [{'item':'Milk','p':2.47,'g':0.16,'offer':'Yes'},
{'item':'Butter','p':4.50,'g':0.32,'offer':'No'},
{'item':'Egg','p':3.40,'g':0.24,'offer':'No'}]
choose = int(input('1.Item = ... ')) - 1 # subtract 1 to get 0-indexing
qnty = int(input('How many do you want?: '))
Next up, there is a lot going on in the line shopping_basket[Dairy_goods[choose]['item']] = ..., which makes it hard to work with. In my comment, I suggested to go for dictionaries to store the items in the shopping basket to make them easier to modify. The new format of the basket would look like
# shopping_basket
{'Milk':{'item':'Milk','p':2.47,'g':0.16,'offer':'Yes', 'qnty':1}}
which is a bit redundant because of the double item name (as key and within values), which is normally considered a bad thing, but in this case it allows for a much easier access to the shopping cart items.
However, it's actually sufficient to store only the quantities in the shopping basket - {'Milk':1, 'Egg':2, ...} - because all the other information is in Dairy_goods.
selection = Dairy_goods[choose] # e.g., {'item':'Milk','p':2.47,'g':0.16,'offer':'Yes'}
item = selection['item'] # e.g., 'Milk'
shopping_basket[item] = shopping_basket.get(item, 0) + qnty
# optional: delete item if the quantity is <= 0
if shopping_basket[item]['qnty'] <= 0:
shopping_basket.pop(item)
Then, the edit part becomes:
item = input('key in an item to edit: ') # e.g., 'Milk'
qnty = int(input(f'Key in the quantity of {item} you want: ')) # did you hear about f-strings? Super useful!
shopping_basket[item] = qnty # set the new quantity
I have your answer!
But first I want to address a major problem in your code:
for shopItem in shopping_basket:
item = str(input('key in an item to edit: '))
for item in shopping_basket: # unneeded
shopping=list(shopping_basket)
qnty = int(input('Key in the quantity of %s you want: ' % shopItem))
shopping[shopItem][0] = qnty # i would get a error here.
print(shopping)`
That internal for loop can be easily remove to get cleaner code.
for shopItem in shopping_basket:
item = str(input('key in an item to edit: '))
shopping=list(shopping_basket)
qnty = int(input('Key in the quantity of %s you want: ' % shopItem))
shopping[shopItem][0] = qnty # i would get a error here.
print(shopping)`
Now for the actual problem:
for shopItem in shopping_basket:
item = str(input('key in an item to edit: '))
shopping = list(shopping_basket[shopItem])
qnty = int(input('Key in the quantity of %s you want: ' % shopItem))
shopping[item] = qnty # Fixed
Originally with this line of code
shopping=list(shopping_basket) you were changing {'Milk': (2, 2.47, 0.16, 'Yes')} to ['Milk'], and then attempting to change a letter in the word milk, which was throwing an error. I fixed that by changing shopping[item][0] to shopping[item].
Have a great day and good luck with your code!

Seperating tuples in list in Python

I want to seperate tuples in list that comes from a sqlite database. But I don't know why I can't seperate them anyway.
Here is the output : [('3:45',), ('4:52',), ('5:42',), ('6:52',)]
I'm pulling that output from sqlite database like this :
asking = "Select SongTimes from Song_List"
self.cursor.execute(asking)
times = list(self.cursor.fetchall())
print(times)
And after that I want to sum all of song times in that list. But I need to acquire them "3:45" like this. After I will seperate them 3,45 like this and the rest is kinda easy. But like I said I need to focus that output for seperating these tuples. That "," is troublemaker I guess.
You can do the following:
times = [('3:45',), ('4:52',), ('5:42',), ('6:52',)]
seconds = 0 # Total number of seconds
# Iterate over all the tuples
for time in times:
time = time[0] # Get first element of tuple
m, s = time.split(":") # split string in two removing ':'
seconds += 60 * int(m) + int(s) # Convert str to int and add to total sum
print(seconds)

How do I print lists side by side in Python?

New user practicing lists and loops. My goal is create two list from my own input (range is 2 for quick testing, but want to get to 10). I have hit a wall on two problems:
I would like my lists to sit side by side ie.
First Name \t\t\t $77
Next Name \t\t\t $16
I don't know how to change my types so that int or float is not iterable error does not persists. I believe it'll help with my coded out max/min statements
My code:
list1 = []
list2 = []
count = 0
for i in range(2):
customers = input("Customers name? ")
list1.append(customers)
spent = float(input("Amount spent? "))
list2.append(spent)
count += spent
averageSpent = count / 2 # change this when changing range
print("Name\t\t\tAmount")
# won't print side by side. how to use zip()?
print((*list1 + list2), sep = "\n")
print("Total spending:\t\t $", count)
print("Average spending:\t $", averageSpent)
# keep getting 'object is not iterable'
#print("Most money spent:\t $", max(spent))
#print("Least money spent:\t $", min(spent))
My output is currently:
Customers name? work
Amount spent? 45
Customers name? please
Amount spent? 65
Name Amount
work
please
45.0
65.0
Total spending: $ 110.0
Average spending: $ 55.0
Thank you!
Regarding the first issue that you have, the best way for you to print the output side by side would be to iterate over the list and print each value in the list. In addition to that, you can use f-strings which is a feature that we added with python 3.6 and it allows you to do stuff like this:
x = 10
print(f'{x+20}')
>> 30
you can read more about f-strings here.
Regarding the second issue that you're facing. You got this error because you were calling the max() function on a single float. the max function should be called for a list.
I've made some adjustments. Here is what the final code looks like:
list1 , list2 = [] , []
max_range = 2
count = 0
for i in range(max_range):
customer_name = input("Customer name: ")
list1.append(customer_name)
spent = float(input("Amount Spent: "))
list2.append(spent)
count += spent
averageSpent = count / max_range
print("Name\t\t\tAmount")
for i in range(len(list1)):
print(f'{list1[i]} \t\t\t$ {list2[i]}')
print("Total spending:\t\t $", count)
print("Average spending:\t $", averageSpent)
print("Most money spent:\t $", max(list2))
print("Least money spent:\t $", min(list2))
Edit: using the zip function might be another option for printing the two outputs side by side using tuples. However, since you've already said that you're new to these topics I think that you should stay away from zip (for the time being) until you feel comfortable with lists.
Printing things side by side is answered here: Print 2 lists side by side
The error about a float or int not being iterable is because you are calling max(spent) instead of max(list2). The function max() expects a list or other iterable object.
bikemule is correct about the iterables. You need to call max with a list, not a single number. (What would max(101) return? It doesn't quite make sense.)
To make the two lists appear side-by-side, you can use zip combined with a for loop. It will turn the lists into sets of tuples that then will appear in an almost-tabular format when printed.
list_a = [1, 2, 3]
list_b = [4, 5, 6]
for col_a, col_b in zip(list_a, list_b):
print("A_Item: %s | B_Item: %d" %(col_a, col_b))
Returns:
A_Item: 1 | B_Item: 4
A_Item: 2 | B_Item: 5
A_Item: 3 | B_Item: 6

Function problems

So I have two lists:
typeList — list of strings of transaction types (addition, subtraction)
valueList — List of values either added or subtracted.
as both value and type are appended to the list at the same index, I need to make a function to calculate the sum of the total value added and the total value subtracted, but I've been really stuck on it for ages trying to get my head around just how to do it.
desiredType is just the type of transaction that is being looked for. As you'll see, I called my function twice with each type.
I understand that the index values need to be obtained and used between the two lists but not sure how to sum the values up.
def showInfo(typeList, valueList, desiredType):
for i in range(len(valueList)):
if typeList[i] == desiredType:
total = total + valueList[i]
return (total)
Call to the function:
if choice == "I": #Prints transaction information
print (showInfo(transactionTypes,transactionAmounts, "Addition"))
print (showInfo(transactionTypes,transactionAmounts, "Subtraction"))
Any help would be appreciated as well as an explanation on how it's done.
You can zip together those 2 lists, then you don't have to keep track of the index you're looking at:
def transaction_total(types, amounts, desired_type):
total = 0
for transaction_type, amount in zip(types, amounts):
if transaction_type == desired_type:
total += amount
return total
However, you're really just filtering values and summing them. Python makes this cleaner using generator expressions and the sum function:
def transaction_total(types, amounts, desired_type):
return sum(amount for transaction_type, amount in zip(types, amounts)
if transaction_type == desired_type)
If you need to keep your data unmodified, you can just make your function more efficient (just one call) :
def showInfo(typeList, valueList):
for i in range(len(valueList)):
if typeList[i] == "Addition":
total_add = total_add + valueList[i]
else:
total_sub = total_sub - valueList[i]
return (total_add, total_sub)

Double variable for loops (tax calculator)

trying to create a tax calculator for logic practice:
import sys
marital = sys.argv[1]
salary = sys.argv[2]
totalTaxes = 0
if marital == "single":
tiers = [415050,413350,190150,91150,37650,9275]
taxrates = [.35,.33,.28,.25,.15,.10]
if salary > tiers[i]:
for i, j in tiers, taxrates:
for n in range(i-(i+1)):
while n > tiers[i]:
totalTaxes += (n * .35)
This makes sense to me when I'm looking at it but that for loop is causing an error. I've seen double-variable for loops before but can't remember how to do them. Do I use enumerate or itertools in some way?
Also, I'm open to any suggestions on how to reduce the number of nesting loops I have here. I'm sure that's doable. Any and all help is greatly appreciated.
For clarity, the for n in range(i,i-(i+1)): portion is to calculate that bracket of tax for that first interval. Essentially I'm calculating that interval's tax rate for each dollar in the interval and adding that to totalTaxes. Not very efficient but it's all I have.
By the way error is ValueError: too many values to unpack
The main problem of the for loop is you misused it. For i in a_list will iterate each item in a_list and populate the item value to i (not the index). Also if you want to combine two lists together in the for loop, you should use zip.
import sys
marital = sys.argv[1]
salary = sys.argv[2]
totalTaxes = 0
if marital == "single":
tiers = [415050,413350,190150,91150,37650,9275]
taxrates = [.35,.33,.28,.25,.15,.10]
for tier, taxrate in zip(tiers, taxrates):
...
I'm not sure what you are doing inside of the for loop. range(i-(i+1)) is identical to range(-1) which means the program will never run the while loop. What your logic is?

Categories

Resources