Python Dictionary and Tuples Addition - python

Need a bit of help on this problem:
I have the following code:
def insertIntoDataStruct(state,job,count,dict):
if not state in dict:
print "adding"
dict[state] = [(job,count)]
else:
for x in range(0, len(dict[state])):
if(dict[state][x][0] == job):
print "hi"
print dict[state][x][0]
print job
print state
print dict[state][x][1]
dict[state][x][1] = dict[state][x][1] + 1
else:
dict[state].append((job,count))
courses = {}
insertIntoDataStruct("CA", "2121", (1), courses)
insertIntoDataStruct("CA", "169521", 1, courses)
insertIntoDataStruct("CA", "2121", 1, courses)
insertIntoDataStruct("TX", "2121", 1, courses)
insertIntoDataStruct("TX", "169521", 1, courses)
insertIntoDataStruct("TX", "262420", 1, courses)
print courses
and I am getting this error:
adding File "test2.py", line 21, in <module>
hi
insertIntoDataStruct("CA", "2121", 1, courses)
2121
File "test2.py", line 13, in insertIntoDataStruct
2121
dict[state][x][1] = dict[state][x][1] + 1
CA
TypeError: 'tuple' object does not support item assignment
1
Process finished with exit code 1
How can I go about fixing : TypeError: 'tuple' object does not support item assignment
The Ideal output of this code should be:
{
'CA': [('2121', 2), ('169521', 1), ('2122', 1)],
'TX': [('2121', 1), ('169521', 1), ('262420', 1)]
}
Thanks for all help!

tuples are immutable. Therefore, when you try to run the line dict[state][x][1] = dict[state][x][1] + 1, where the left side is the tuple ('2121', 1), the 'tuple' object does not support item assignment error results.

dict[state][x][1] = dict[state][x][1] + 1
You cannot do item assignment here.
My understanding is you want to increment the job ID and add the course in the dictionary with the new job, what you can do is
new_job = str(int(dict[state][x][1])+1)
dict[state].append((new_job, count))
Here I have incremented the job and then appended into the dictionary

Related

Passing array contents into function not working correctly

When trying to pass an array into a function it doesn't do anything at all. My code is below
main.py
import items
import npcs.py
def pickup(item):
global player_weight_max,player_weight,player_inv
#Calculates if item's weight will make player_weight go over player_weight_max
if player_weight + item[5] <= player_weight_max:
player_inv.append(item)
player_weight = player_weight + item[5]
else:
print("You're not able to carry this item.")
def npc(npc):
#Prints NPC speech
if npc[2] != None:
print("".join(npc[1]) + ": " + "".join(npc[2]))
else:
pass
for function in npc[3]:
if function[0] == 'pickup':
pickup(function[1])
if function[0] == 'battle':
battle(function[1])
npcs.py
import items
#art,name,speech,functions
test_guy = [["art"],["name"],["speech"],[
[['pickup'],[items.armour[0]],
[['pickup'],[items.armour[1]],
]
]
items.py
armour = [
[str(""),str("Tin Helmet"),int(1),int(20),str("head"),int(2),int(0),int(2)],
[str(""),str("Tin Chestplate"),int(1),int(20),str("torso"),int(0),int(1),int(2)],
[str(""),str("Tin Pants"),int(1),int(20),str("legs"),int(3),int(0),int(2)],
[str(""),str("Tin Boots"),int(1),int(20),str("feet"),int(2),int(0),int(2)],
]
Why is pickup() not appending the information obtained from items.py
I have already verified pickup() works when doing pickup(items.armour[0]) which should just be passing the array at that location into pickup(), why can the same not be done with the information contained in test_guy[3][0] and test_guy[3][1]?
updated the code as follows:
def npc(npc):
#Prints NPC speech
if npc[2] != None:
print("".join(npc[1]) + ": " + "".join(npc[2]))
else:
pass
for function in npc[3]:
print(function[1][0])
if function[0][0] == 'pickup':
pickup(function[1][0])
if function[0][0] == 'battle':
battle(function[1][0])
upon running:
npc(npcs.test_guy)
print(player_inv)
the output is now correct:
name: speech
['', 'Tin Helmet', 1, 20, 'head', 2, 0, 2]
['', 'Tin Chestplate', 1, 20, 'torso', 0, 1, 2]
[['', 'Tin Helmet', 1, 20, 'head', 2, 0, 2], ['', 'Tin Chestplate', 1, 20, 'torso', 0, 1, 2]]
Thank you!
(Yes I know this is not the most efficient way to do things, I'm still learning and just trying to get things working for now)
Whilst avoiding the unnecessary complexity of this code..
Your items are within another array so item[0] will never equal pickup/battle and equally, item[1] will never be the item, it will be an array with an item.
So first fix the missing brackets of your functions part of test_guy, and then reference the functions inner array first
item[0][0] and item[0][1]

Text to cleaner text to excel spreadsheet project

I have a text file full of pc data, organized as a list of blocks of one of two types. Either:
*redacted*
My goal was to have Python (3.6.2) open and read the file, clean it up, and compile the data into an excel spreadsheet as follows:
Column 1: PC name
Column 2: Error Type (0 if none, 1-4 for 4 error types)
Column 3: ID (if no error, no braces containing the ID)
Column 4: Password (if no error, just the password)
Here is my code. I use Pycharm, and am in a virtual env:
import xlsxwriter
workbook = xlsxwriter.Workbook('Computer Data.xlsx')
worksheet = workbook.add_worksheet()
bold = workbook.add_format({'bold': True})
left = workbook.add_format({'align': 'justify'})
worksheet.set_column(0, 0, 14)
worksheet.set_column(1, 1, 5)
worksheet.set_column(2, 2, 38)
worksheet.set_column(3, 3, 55)
worksheet.write('A1', 'Name', bold)
worksheet.write('B1', 'Error', bold)
worksheet.write('C1', 'ID', bold)
worksheet.write('D1', 'Password', bold)
def nonblank_lines(f):
for l in f:
line = l.rstrip()
if line:
yield line.lstrip
with open("C:\\Users\\MyName\\Desktop\\BLRP.txt", "r+") as op:
gold_lst = []
nonblank = nonblank_lines(op)
for line in nonblank:
if line.startswith("Computer Name"):
gold_lst.append(str(line))
gold_lst.append("NO ERROR")
elif line.startswith("ID"):
gold_lst.append("IDG: " + str(line))
gold_lst.append('NO ERROR')
elif line.startswith("ERROR: An error occurred while"):
gold_lst.append('1')
gold_lst.append(str('ID: {' + line + '}'))
gold_lst.append(str('Password: '))
elif line.startswith("ERROR: No key"):
gold_lst.append('2')
gold_lst.append(str('ID: {' + line + '}'))
gold_lst.append(str('Password: '))
elif line.startswith("ERROR: An error occurred (code 0x80070057)"):
gold_lst.append('3')
gold_lst.append(str('ID: {' + line + '}'))
gold_lst.append(str('Password: '))
elif line.startswith("ERROR: An error occurred (code 0x8004100e)"):
gold_lst.append('4')
gold_lst.append(str('ID: {' + line + '}'))
gold_lst.append(str('Password: '))
elif line.startswith("Password"):
gold_lst.append(str('Password: ' + next(nonblank)))
print(gold_lst)
op.close()
pc_data = (gold_lst)
row = 1
col = 0
for obj in pc_data:
if obj.startswith("Computer Name"):
worksheet.write_string(row, col, obj[15:])
elif obj.startswith('NO'):
worksheet.write_number(row, col + 1, 0, left)
elif obj.startswith('1'):
worksheet.write_number(row, col + 1, int(obj), left)
elif obj.startswith('2'):
worksheet.write_number(row, col + 1, int(obj), left)
elif obj.startswith('3'):
worksheet.write_number(row, col + 1, int(obj), left)
elif obj.startswith('4'):
worksheet.write_number(row, col + 1, int(obj), left)
elif obj.startswith("ID: {ERROR"):
worksheet.write_string(row, col + 2, '')
elif obj.startswith("IDG: "):
worksheet.write_string(row, col + 2, obj[10:-1])
elif obj.startswith("Password"):
worksheet.write_string(row, col + 3, obj[9:])
row += 1
workbook.close()
Now, this works perfectly for the file in question, but, in addition to the terribly suboptimal code, I'm sure, there is something I can explicitly see that needs improved. In this block:
if line.startswith("Computer Name"):
gold_lst.append(str(line))
gold_lst.append("NO ERROR")
I only want "NO ERROR" to be appended to my list if my line starts with "Computer Name" AND the next non-blank line does not begin with "ERROR." Naturally, I tried this:
if line.startswith("Computer Name"):
if next(nonblank).startswith("ERROR"):
gold_lst.append(str(line))
elif next(nonblank).startswith("VOLUME"):
gold_lst.append(str(line))
gold_lst.append("NO ERROR")
The problem is, this creates a jacked up excel spreadsheet, and I don't at all know why. Even in the step afterward in the main code where I print gold_lst (just to check if the list is correct), the list is terribly inaccurate. I can't even seem to figure out of what the list is comprised.
How can I fix this?
As for a second question, if I may ask it in the same topic, more general text files of this type which I am likely to receive in the future may contain computers with more than one ID and password. The block would look like this, if I had to guess:
*redacted*
And there may be even more than 2 such ID/Password combos. How can I modify my code to allow for this? As it stands, my code will not easily account for this. I am quite new to Python, so maybe it could, but I don't see it.
One approach to this problem is as follows:
Read in the whole file, skipping any empty lines.
Use Python's groupby() function to split the list of lines into blocks based on the Computer Name line.
For each block, try and extract both an error and a list of IDs and Passwords. Leave blank if not present.
For each block, write any extracted data to the next row in the spreadsheet.
The script is as follows:
from itertools import groupby
import xlsxwriter
import re
workbook = xlsxwriter.Workbook('Computer Data.xlsx')
worksheet = workbook.add_worksheet()
bold = workbook.add_format({'bold': True})
left = workbook.add_format({'align': 'justify'})
cols = [('Name', 14), ('Error', 5), ('ID1', 38), ('Password1', 55), ('ID2', 38), ('Password2', 55), ('ID3', 38), ('Password3', 55)]
for colx, (heading, width) in enumerate(cols):
worksheet.write_string(0, colx, heading, bold)
worksheet.set_column(colx, colx, width)
rowy = 1
lines = []
data = []
computer_name = None
with open('BLRP.txt') as f_input:
lines = [line.strip() for line in f_input if len(line.strip())]
for k, g in groupby(lines, lambda x: x.startswith("Computer Name:")):
if k:
computer_name = re.search(r'Computer Name:\s*(.*)\s*', list(g)[0]).group(1)
elif computer_name:
block = list(g)
error = 'NO ERROR'
ids = []
passwords = []
for line_number, line in enumerate(block):
re_error = re.match('ERROR:\s+"(.*?)"', line)
if re_error:
error = re_error.group(1)
if line.startswith('Numerical Password:'):
ids.append(re.search('\{(.*?)\}', block[line_number+1]).group(1))
passwords.append(block[line_number+3].strip())
worksheet.write_string(rowy, 0, computer_name)
worksheet.write_string(rowy, 1, error)
for index, (id, pw) in enumerate(zip(ids, passwords)):
worksheet.write_string(rowy, index * 2 + 2, id)
worksheet.write_string(rowy, index * 2 + 3, pw)
rowy += 1 # Advance to the next output row
workbook.close()
Assuming your BLRP.txt is as follows:
Computer Name: "Name Here1"
ERROR: "some type of error"
Blah blah
Blah blah
Blah blah
Computer Name: "Name Here2"
Volume blah blah
Blah Blah
Numerical Password:
ID: {"The ID1 is here; long string of random chars"}
Password:
"Password1 here; also a long string"
Blah Blah
Blah Blah
Numerical Password:
ID: {"The ID2 is here; long string of random chars"}
Password:
"Password2 here; also a long string"
Blah Blah
Blah Blah
Numerical Password:
ID: {"The ID3 is here; long string of random chars"}
Password:
"Password3 here; also a long string"
Blah Blah
Blah Blah
You would get a spreadsheet as follows:
How does groupby() work?
Normally when you iterate over a list, it gives you the entries one item at a time. With groupby(), you are able to iterate over this list in "groups", where the number of items in each group is based on a condition. The condition is provided in the form of a function (I have used lambda to avoid writing a separate function).
groupby() will build up the group of items to return until the result from the function changes. In this case, the function is looking for lines that start with the word Computer Name. So when that is true it will return with one item (unless there are two adjacent lines with Computer Name on them). Next it will return with all the lines that don't start with Computer Name, and so on.
It returns two things, a key and a group. The key is the result of the function startswith(), which will either be True or False. The group is an iterable holding all the matching items. list(g) is used to convert it into a normal list, in this case all the lines until the next Computer Name line is returned.
To write the entries onto different rows and to convert known error messages into numbers:
from itertools import groupby
import xlsxwriter
import re
workbook = xlsxwriter.Workbook('Computer Data.xlsx')
worksheet = workbook.add_worksheet()
bold = workbook.add_format({'bold': True})
left = workbook.add_format({'align': 'justify'})
cols = [('Name', 14), ('Error', 5), ('ID', 38), ('Password', 55)]
for colx, (heading, width) in enumerate(cols):
worksheet.write_string(0, colx, heading, bold)
worksheet.set_column(colx, colx, width)
rowy = 1
lines = []
data = []
computer_name = None
error_numbers = {
'An error occurred while connecting to the BitLocker management interface.' : 1,
'No key protectors found.' : 2,
'An error occurred (code 0x80070057):' : 3,
'An error occurred (code 0x8004100e):' : 4}
with open('BLRP.txt') as f_input:
lines = [line.strip() for line in f_input if len(line.strip())]
for k, g in groupby(lines, lambda x: x.startswith("Computer Name:")):
block = list(g)
if k:
computer_name = re.search(r'Computer Name:\s*(.*)\s*', block[0]).group(1)
elif computer_name:
error_number = 0 # 0 for NO ERROR
ids = []
passwords = []
for line_number, line in enumerate(block):
re_error = re.match('ERROR:\s+?(.*)\s*?', line)
if re_error:
error = re_error.group(1)
error_number = error_numbers.get(error, -1) # Return -1 for an unknown error
if line.startswith('Numerical Password:'):
ids.append(re.search('\{(.*?)\}', block[line_number+1]).group(1))
passwords.append(block[line_number+3].strip())
worksheet.write_string(rowy, 0, computer_name)
worksheet.write_number(rowy, 1, error_number)
for id, pw in zip(ids, passwords):
worksheet.write_string(rowy, 0, computer_name)
worksheet.write_number(rowy, 1, error_number)
worksheet.write_string(rowy, 2, id)
worksheet.write_string(rowy, 3, pw)
rowy += 1 # Advance to the next output row
if len(ids) == 0:
rowy += 1 # Advance to the next output row
workbook.close()

list comprehension fails but why?

Who can explain to me why this list comprehension fails:
provider1 = {'id': 1, 'name': 'Een'}
provider2 = {'id': 2, 'name': 'Twee'}
provider3 = {'id': 3, 'name': 'Drie'}
provider4 = {'id': 4, 'name': 'Vier'}
provider5 = {'id': 5, 'name': 'Vijf'}
provider6 = {'id': 6, 'name': 'Zes'}
provider7 = {'id': 7, 'name': 'Zeven'}
providers = [provider1, provider2, provider3, provider4, provider5, provider6, provider7]
def testfunc(id):
return next(provider for provider in providers if int(provider['id']) == int(id))
for x in range(0, 8):
print testfunc(x)
When I run this and 0 is passed to the funtion, the output from this is:
Traceback (most recent call last):
File "/Users/me/Documents/scratchpad/main.py", line 17, in <module>
print testfunc(x)
File "/Users/me/Documents/scratchpad/main.py", line 13, in testfunc
return next(provider for provider in providers if int(provider['id']) == int(id))
StopIteration
Process finished with exit code 1
It does work for a non zero integer.
That's because next function raises StopIteration when there's no next item. In particular this occures when the underlying iterator is empty which is your case for id == 0.
The dictionary does not have a value for key 0. It finds value as None and it cannot determine the next value for iteration.
Replace your code with valid ranges, your code will work
for x in range(1, 8):
print( testfunc(x))
OR
You could add provider0 = {'id': 0, 'name': 'Onkar'}
and providers = [provider0,provider1, provider2, provider3, provider4, provider5, provider6, provider7] to make
for x in range(0, 8):
print( testfunc(x))
work
Yes, because your generator is empty. None of your data matches
if int(provider['id']) == 0
Calling next on an empty generator throws the StopIteration.

Error 'numpy.int32' object does not support item assignment

I get this error
Traceback (most recent call last):
File "C:\Users\User1\Desktop\cellh5_scripts\ewa_pnas_fate.py", line 90, in <module>
ec.combine_classifiers("Event labels combined")
File "C:\Users\User1\Desktop\cellh5_scripts\ewa_pnas_fate.py", line 53, in combine_classifiers
pnas_class[pnas_class==3] = 1
TypeError: 'numpy.int32' object does not support item assignment
by runing the code
def combine_classifiers(self, output_name):
all_combined_classes = []
for _, (plate_name, w, p, t1, t2, track_ids, track_labels) in self.mapping[['Plate',
'Well',
'Site',
'Gene Symbol',
'siRNA ID',
'Event track ids',
'Event track labels']].iterrows():
combined_classes = []
ch5_file_handle = self.cellh5_handles[plate_name]
ch5_pos = ch5_file_handle.get_position(w, str(p))
for track_id, track_label in zip(track_ids, track_labels):
h2b_class = track_label.copy()
print(track_id)
pnas_class = ch5_pos.get_class_prediction('secondary__expanded')[track_id]['label_idx'] + 1
print(pnas_class)
inter_idx = h2b_class == 1
pnas_class[pnas_class==3] = 1
pnas_class[pnas_class==2]+=2
combined_class = h2b_class
combined_class[inter_idx] = pnas_class[inter_idx]
combined_classes.append(combined_class)
all_combined_classes.append(combined_classes)
self.mapping[output_name] = pandas.Series(all_combined_classes)
I print pnas_class which is 1, and track_id which is 50708. I'm wondering what the designer of code want to do in the part:
inter_idx = h2b_class == 1
pnas_class[pnas_class==3] = 1
pnas_class[pnas_class==2]+=2
combined_class = h2b_class
combined_class[inter_idx] = pnas_class[inter_idx]
How can I change that to have the same meaning?
pnas_class is a an integer so you can't select item from an integer by [pnas_class==3] = 1.
Maybe you are trying to affect 1 to pnas_class if it's equal to 3. In this case try this:
pnas_class= 1*(pnas_class == 3) + pnas_class*(pnas_class != 3 )
Ok I found the mistake. You arer right the pnas_class should not be an integer and I know why is it integer instead of array.

Handling and bypassing "TypeError: 'NoneType' " in python code

So I have this code which prints out the minimum cost and restaurant id for the item/items. The customer doesnt want to visit multiple restaurants. So for example if he asks for "A,B" then the code should print shop which offers them both , instead of scattering the user requirement around different restaurants (even if some restaurant is offering it cheap).
The error is basically coming because both the item ('burger' and 'D') are not available "together" at any of the two restaurants (1 & 2). But instead of throwing such long error , I'd simply like to print "One of item not available at a resto" etc.
Other such error throwing cominations are solver(shop_text,['tofulog', 'D']) , because 'tofulog' is only available at restaurant_1, wheras 'D' is available only at restaurant_2.
def build_shops(shop_text):
shops = {}
for item_info in shop_text:
shop_id,cost,items = item_info.replace('\n', '').split(',')
cost = float(cost)
items = items.split('+')
if shop_id not in shops:
shops[shop_id] = {}
shop_dict = shops[shop_id]
for item in items:
if item not in shop_dict:
shop_dict[item] = []
shop_dict[item].append([cost,items])
return shops
def solve_one_shop(shop, items):
if len(items) == 0:
return [0.0, []]
all_possible = []
first_item = items[0]
if first_item in shop:
for (price,combo) in shop[first_item]:
#print "items,combo=",items,combo
sub_set = [x for x in items if x not in combo]
#print "sub_set=",sub_set
price_sub_set,solution = solve_one_shop(shop, sub_set)
solution.append([price,combo])
all_possible.append([price+price_sub_set, solution])
if all_possible:
cheapest = min(all_possible, key=(lambda x: x[0]))
return cheapest
def solver(input_data, required_items):
shops = build_shops(input_data)
result_all_shops = []
for shop_id,shop_info in shops.iteritems():
this_shop = solve_one_shop(shop_info, required_items)
if this_shop is not None:
(price, solution) = this_shop
result_all_shops.append([shop_id, price, solution])
shop_id,total_price,solution = min(result_all_shops, key=(lambda x: x[1]))
print('SHOP_ID=%s' % shop_id)
sln_str = [','.join(items)+'(%0.2f)'%price for (price,items) in solution]
sln_str = '+'.join(sln_str)
print(sln_str + ' = %0.2f' % total_price)
shop_text = open('input-1.csv','rb')
solver(shop_text,['burger', 'D'])
=====input-1.csv=====restaurant_id, price, item
1,2.00,burger
1,1.25,tofulog
1,2.00,tofulog
1,1.00,chef_salad
1,1.00,A+B
1,1.50,A+CCC
1,2.50,A
2,3.00,A
2,1.00,B
2,1.20,CCC
2,1.25,D
======OUTPUT=======
Traceback (most recent call last):
File "26mar_cheap.py", line 106, in <module>
final_out(restaurant_read,sys.argv[2:])
File "26mar_cheap.py", line 92, in final_out
this_resto = requirement_one_restaurant(shop_info, required_items)
File "26mar_cheap.py", line 77, in requirement_one_restaurant
cost_sub_set,solution = requirement_one_restaurant(shop, sub_set)
TypeError: 'NoneType' object is not iterable
The traceback doesn't seem to be relevant to the code you've posted: the error is in requirement_one_restaurant but you've only posted solve_one_shop.
However, assuming the pattern of the code is similar, you're apparently doing some sort of recursive call. When you do this you need to ensure that all possible paths return a value. In solve_one_shop, for example, if all_possible is still empty by the end of the function then nothing will be returned, which will lead to the NoneType error.

Categories