change/writing single value to csv file python - python

There is an error in my code :
_csv.Error: sequence expected
which i believe is because i am trying to write only one value not a list etc.
exRtFile = open ('exchangeRate.csv')
exchReader = csv.reader(exRtFile)
exchWriter = csv.writer(exRtFile)
loop2=0
while loop2==0:
selected=int(input("Please select an option: "))
if selected == 1:
change = input("What rate would you like to change: ")
changeRt = float(input("What would you like to change the rate to: "))
for row in exchReader:
currency = row[0]
if currency == change:
crntRt = row[1]
crntRt = changeRt
exchWriter.writerow(crntRt)
exRtFile.close()
what would be the best way to fix this, or is there a better wayy to change a value in an CSV file?
csv file:
Pound Sterling,1
Euro,1.22
US Dollar,1.67
Japanese Yen,169.948

Here is some code, not tested, that will do what you want. The idea is to read the text into memory, apply the updates, then write out the results over the original file.
You can further enhance this ask the user if they want to save their changes, and to add new currencies instead of just telling the user they're not known.
In the real world, I would break this code into three separate functions (or even classes), one for reading, one for writing, and one for editing the list.
import csv
rates = {}
# read file into dictionary
with open('csv_file.csv', 'r') as in_file:
rdr = csv.reader(in_file)
for item in reader:
rates[row[0]] = row[1]
# ask user for updates and apply to dictionary
while true:
cmd = raw_input('Enter exchange rate to adjust, or blank to exit')
if cmd is None or cmd.strip() == '':
break
if rates.has_key(cmd):
new_rate = float(raw_input('Enter new exchange rate:'))
rates[cmd] = new_rate
else:
print 'Currency {} is not known.'.format(cmd)
# Write the updated dictionary back over the same file.
with open('csv_file.csv', 'w') as out_file:
wrtr = csv_writer(out_file)
wrtr.writerows(rates)

Answering your question: Yes, the problem is that you were trying to write only a value, while writerow expects a list.
That said... Would you consider changing a bit the way your code works?
Here's what I've done (I've tested it now, so I know it works):
First, ask the user for all the changes to make and keep them in a dict where keys are the currency names (Euro, for instance) and the value is the new currency value (5.0, for instance) The user can get out of the loop pressing 0
Second, open and read your exchangeRate.csv file row by row. If the row[0] (name of the currency) is among the values to change, then change it in that row.
No matter what happens (regardless of whether the row needed to be changed or not) write that row in a new temporary file exchangeRate.csv.tmp
When all the rows in the original file are read, you'll have exchangeRate.csv.tmp with some rows unchanged and some rows changed. Swap (move) the .tmp file to exchangeRate.csv
Dunno... might be too much change maybe? Here it is, anyway:
import csv
import shutil
change_rates = {}
selected = 1
while selected:
selected=int(raw_input("Please select an option: (1 to change, 0 to exit)"))
if selected == 1:
change = raw_input("What rate would you like to change?: ")
changeRt = float(raw_input("What would you like to change the rate to: "))
change_rates[change] = changeRt
if len(change_rates) > 0:
with open('exchangeRate.csv', 'r') as f_in,\
open('exchangeRate.csv.tmp', 'w') as f_out:
exchReader = csv.reader(f_in)
exchWriter = csv.writer(f_out)
for row in exchReader:
if row[0] in change_rates:
row[1] = change_rates[row[0]]
exchWriter.writerow(row)
shutil.move('exchangeRate.csv.tmp', 'exchangeRate.csv')
And a sample execution below:
Please select an option: (1 to change, 0 to exit)1
What rate would you like to change?: Euro
What would you like to change the rate to: 5
Please select an option: (1 to change, 0 to exit)0
borrajax#borrajax:~/Documents/Tests$ cat ./exchangeRate.csv
Pound Sterling,1
Euro,5.0
US Dollar,1.67
Japanese Yen,169.948
You can always make more optimizations, such as... allow case insensitive searches, or check that the currency has actually been changed (like even if the user says he wants to change the currency Euro to 5.0, if that was the Euro's exchange rate then don't do anything)... Things like that.
EDIT 1:
I've just seen Larry Lustig's answer and I agree that for small files as it seems to be your case (files that you can fully load in memory) the continuous reading and writing from disk I posted is not optimal. His idea of keeping everything in memory and then do a bulk write to the same exchangeRate.csv file probably is a better fit for your needs.
EDIT 2:
To answer your questions in a comment to this answer:
what does .tmp do at the end of: exchangeRate.csv.tmp:
It's just a new name. I add the suffix .tmp to avoid a naming conflict with your original file (exchangeRate.csv). You could name it whatever you want, though (even foobar.baz)
What is the purpose of 'change' in the variable: change_rates[change] = changeRt:
change is a variable that contains the name of the currency to change (in the usage example I posted, change contains the string "Euro", because that's what the user (erm... me) typed on the console. Is just a way of accessing a dict.
What is the prupose of '[row[0]]' in: row1=change_rates[row[0]].
We agreed that when reading the file, row[0] (just like that, not [row[0]]) contains the name of the currency in the file (Euro, Pound Sterling... etcetera) right? So at a certain point of the execution row[0] will contain the string "Euro", which (in my test example) is the currency the user wanted to change. That string ("Euro") is also a key in the change_rates dictionary (because the user said he wanted to change it) so you are querying the value for the item with key "Euro" in the change_rates dictionary (which will give you 5.0). Is pretty much doing change_rates["Euro"] To see it a bit more clearer add the line print "Currencies to change: %s" % change_rates on the line right above if len(change_rates) > 0: (that'll show you how the dictionary looks like)
what does shutil.move('exchangeRate.csv.tmp', 'exchangeRate.csv') do?
It copies the file with the new currencies to exchangeRate.csv (see the shutil documentation)

Related

How can I change the specific word from the text file using loops and if statements?

I am doing a simple project for my first python course and I am stuck in one part which I have no idea how to continue.
So in this part user should input a vehicle id that he/she wants to rent. After putting the vehicle ID, my code starts to search for that vehicle ID in the Vehicle.txt text file. If the code finds the VehicleID variable and also finds that it is "A" (which means available), it starts printing details for that specific car.
My Vehicle.txt text file looks like this;
XJY-604,Hyundai Getz,O,0,45.00,A
AJB-123,BMW mini,P,200,65.00,A
WYN-482,Ford Fiesta,O,0,40,A
BKV-943,Ford Ikon,P,150,60,A
JMB-535,Ford Flex,O,0,50,A
FKI-232,Fiat Egea,O,0,30,A
KLE-154,Toyota Corolla,O,0,40,A
ITO-444,Renault Clio,O,0,55,A
MAA-321,Honda Civic,O,0,70,A
IRK-948,Hyundai i20,O,0,30,A
TRY-475,Peugeot 2008,O,0,50,A
IBM-984,Skoda Superb,O,0,60,A
KRI-365,Opel Corsa,O,0,50,A
PMA-760,Citreon C3,O,0,55,A
NGT-407,KIA Sportage,O,0,60,A
So until this far, everything is fine; if the code finds the Vehicle ID (and the condition "A") then the code starts to print details as I want (if the condition is different or vehicle ID is not found it also prints appropriate error massages which are also perfectly working as I want).
My problem starts after this part:
After printing the details from the car, my code should change that specific car's condition from "A" (available) to "R" (rented) in the Vehicle.txt text file.
For example, let's say the user entered the Vehicle ID TRY-475 --> After this input, my excepted change in the Vehicle.txt text file is;
excepted change
But the actual change in the text file is;
actual change
My code looks like this;
from datetime import datetime
now = datetime.now()
dateandtime = now.strftime("%d/%m/%Y %H:%M:%S")
def rentVehicle():
VehicleID = input("Please enter the vehicle ID you want to rent: ")
with open("Vehicle.txt","r+") as f1:
for line in f1:
l = line.split(",")
if l[0] == VehicleID and l[5] == "A\n" or l[5] == "A":
renterID = input("Please enter your ID: ")
startingodometer = input("Please enter the current odometer reading: ")
print("\nCar",l[0],"is rented to", renterID,"\n")
print("\t\t******************** Vehicle Details ********************\n")
print("Vehicle ID =",l[0],"\t\tDescription =",l[1],"\t\tDaily Rate =",l[4],"\tStatus =",l[5],"\n")
print("Renter ID =",renterID,"\tDate/time of rent =",dateandtime,"\tRent Starting Odometer =",startingodometer)
f1.write(l[5].replace(l[5],"R\n"))
print("\nRenting vehicle is successful!")
break
elif l[0] == VehicleID and l[5] == "R\n" or l[5] == "R":
print("\nThe vehicle you entered is rented. Please display available cars from the main menu.")
break
else:
print("\nThe vehicle ID you entered does not exist. Please enter a valid vehicle ID.")
break
rentVehicle()
I think the problem is in line 17 ( f1.write(l[5].replace(l[5],"R\n"))). I searched for the other options but they also didn't give my excepted output in the Vehicle.txt text file. Additionally, I am not allowed to change my file name or add these lines to another file (manually or in the code) as it is restricted in my project work. I should only update the current Vehicle.txt via code. I would be very glad if someone solve this. For a beginner, these problems are really challenging sometimes. Thanks in advance.
the problem in your code is here:
with open("Vehicle.txt","r+") as f1:
for line in f1:
l = line.split(",")
if l[0] == VehicleID and l[5] == "A\n" or l[5] == "A":
renterID = input("Please enter your ID: ")
startingodometer = input("Please enter the current odometer reading: ")
~~~~~~~~~~~~~~~~~~~
f1.write(l[5].replace(l[5],"R\n"))
~~~~~~~~~~~~~~~~~~~
break
elif l[0] == VehicleID and l[5] == "R\n" or l[5] == "R":
print("\nThe vehicle you entered is rented. Please display available cars from the main menu.")
break
else:
print("nothing found")
break
I supposed that you are not very familiar with how does the file reading work on the background ... To put it simply, there is a buffer of a specific length (for example 1028B) whihc means it is going to read 1028B of text. The reason why it is this way is that you are unable to efficiently know, how long the line will be, and also reading from a file is slow, therefore reading as much as possible in the shortest time possible is what everyone is aiming for.
Now to your code, what happend there is that your whole file got loaded into the mentioned buffer and a file pointer ended up at the end of the file.
What you should do and is recommended to do is not to rewrite the file you are currently reading (you can check out some articles about how files actually work, sorry I am not 100% sure right now whether it even lets you to write to a file that you are reading ...).
Therefore, what you need to do, is this (this is pseudocode, in order for you to do it yourself, to gain more experience :)):
with open("Vehicle.txt","r+") as read_file:
with open("Vehicle2.txt","w+") as write_file:
for line in read_file:
if (your checks):
... (setting up)
write_file.write(f"{l[0]},{l[1]},{l[2]},{l[3]},{l[4]},{edited_thing}")
else:
write_file.write(line)
// after this, you can eighter rename these files or delete the original one and then rename the other one ther:
// vehicle.txt -> temp.txt ; Vehicle2.txt => Vehicle.txt ; delete temp.txt ?
I hope this answer helps and I wish you a nice programming journey ;)
EDIT:
I just noticed that you have multiple checks there. if you do not really need to break it, I recommend you using continue which will immediatelly start the next iteration.

Error is being shown when I run the code and I am unable to figure out the second question

import pickle
med = {}
medfile = open("Medicines.dat","wb")
while True:
name = input("Enter the name: ")
company = input("Enter the company: ")
chemical = input("Enter the chemical: ")
price = input("Enter the price: ")
med['name'] = name
med['company'] = company
med['chemical'] = chemical
med['price'] = price
pickle.dump(med,medfile)
ans = input("Wouldyou like to add more(y/n) ")
if ans == "y":
continue
elif ans == "n":
break
medfile = open("Medicines.dat","r+")
print(pickle.load(medfile))
medfile.close()
The question is as follows:
A binary file "Medicines.dat has structure [Name, Company, Chemical, Price] a) Write a user defined function add_data() to input the data for a record and store in the file b) Write a function search() which accepts a company name and displays the details of all the Medicines by that company
There are a few problems here:
1st Opening the file correctly
medfile = open("Medicines.dat","r+")
You mean rb. The difference is explained here, but pickle parsing requires the file to be in "binary" mode, hence the "b".
2nd Closing the file correctly
You should close the file before re-opening it for writing, as a matter of best practce. (medfile.close()). Even better, python will take care of when the file gets closed if you use the "with" syntax to create a context
3rd Having the right values
While the code should now run, I doubt it will do what you want. Your query asks "Wouldyou [sic] like to add more(y/n)", but it does not look to me like it is adding more values, since you use the same "med" dictionary over and over. Consider how the "new" fields would ever be distinguishable from the "old" ones, based on their key

Python - program for searching for relevant cells in excel does not work correctly

I've written a code to search for relevant cells in an excel file. However, it does not work as well as I had hoped.
In pseudocode, this is it what it should do:
Ask for input excel file
Ask for input textfile containing keywords to search for
Convert input textfile to list containing keywords
For each keyword in list, scan the excelfile
If the keyword is found within a cell, write it into a new excelfile
Repeat with next word
The code works, but some keywords are not found while they are present within the input excelfile. I think it might have something to do with the way I iterate over the list, since when I provide a single keyword to search for, it works correctly. This is my whole code: https://pastebin.com/euZzN3T3
This is the part I suspect is not working correctly. Splitting the textfile into a list works fine (I think).
#IF TEXTFILE
elif btext == True:
#Split each line of textfile into a list
file = open(txtfile, 'r')
#Keywords in list
for line in file:
keywordlist = file.read().splitlines()
nkeywords = len(keywordlist)
print(keywordlist)
print(nkeywords)
#Iterate over each string in list, look for match in .xlsx file
for i in range(1, nkeywords):
nfound = 0
ws_matches.cell(row = 1, column = i).value = str.lower(keywordlist[i-1])
for j in range(1, worksheet.max_row + 1):
cursor = worksheet.cell(row = j, column = c)
cellcontent = str.lower(cursor.value)
if match(keywordlist[i-1], cellcontent) == True:
ws_matches.cell(row = 2 + nfound, column = i).value = cellcontent
nfound = nfound + 1
and my match() function:
def match(keyword, content):
"""Check if the keyword is present within the cell content, return True if found, else False"""
if content.find(keyword) == -1:
return False
else:
return True
I'm new to Python so my apologies if the way I code looks like a warzone. Can someone help me see what I'm doing wrong (or could be doing better?)? Thank you for taking the time!
Splitting the textfile into a list works fine (I think).
This is something you should actually test (hint: it does but is inelegant). The best way to make easily testable code is to isolate functional units into separate functions, i.e. you could make a function that takes the name of a text file and returns a list of keywords. Then you can easily check if that bit of code works on its own. A more pythonic way to read lines from a file (which is what you do, assuming one word per line) is as follows:
with open(filename) as f:
keywords = f.readlines()
The rest of your code may actually work better than you expect. I'm not able to test it right now (and don't have your spreadsheet to try it on anyway), but if you're relying on nfound to give you an accurate count for all keywords, you've made a small but significant mistake: it's set to zero inside the loop, and thus you only get a count for the last keyword. Move nfound = 0 outside the loop.
In Python, the way to iterate over lists - or just about anything - is not to increment an integer and then use that integer to index the value in the list. Rather loop over the list (or other iterable) itself:
for keyword in keywordlist:
...
As a hint, you shouldn't need nkeywords at all.
I hope this gets you on the right track. When asking questions in future, it'd be a great help to provide more information about what goes wrong, and preferably enough to be able to reproduce the error.

Organizing and printing information by a specific row in a csv file

I wrote a code that takes in some data, and I end up with a csv file that looks like the following:
1,Steak,Martins
2,Fish,Martins
2,Steak,Johnsons
4,Veggie,Smiths
3,Chicken,Johnsons
1,Veggie,Johnsons
where the first column is a quantity, the second column is the type of item (in this case the meal), and the third column is an identifier (in this case it is family name). I need to print this information to a text file in a specific way:
Martins
1 Steak
2 Fish
Johnsons
2 Steak
3 Chicken
1 Veggie
Smiths
4 Veggie
So What I want is the family name followed by what that family ordered. I wrote the following code to accomplish this, but it doesn't seem to be quite there.
import csv
orders = "orders.txt"
messy_orders = "needs_sorting.csv"
with open(messy_orders, 'rb') as orders_for_sorting, open(orders, 'a') as final_orders_file:
comp = []
reader_sorting = csv.reader(orders_for_sorting)
for row in reader_sorting:
test_bit = [row[2]]
if test_bit not in comp:
comp.append(test_bit)
final_orders_file.write(row[2])
for row in reader_sorting:
if [row[2]] == test_bit:
final_orders_file.write(row[0], row[1])
else:
print "already here"
continue
What I end up with is the following
Martins
2 Fish
Additionally, I never see it print "already here" though I think I should if it were working properly. What I suspect is happening is that the program goes through the second for loop, then exits the program without continuing the first loop. Unfortunately I'm not sure how to make it go back to the original loop once it has identified and printed all instances of a given family name in a file. I thought The reason I have it set up this way, is so that I can get the family name written as a header. Otherwise I would just sort the file by family name. Please note that after running the orders through my first program, I did manage to sort everything such that each row represents the complete quantity of that type of food for that family (there are no recurring instances of a row containing both Steak and Martins).
This is a problem that you solve with a dictionary; which will accumulate your items by the last name (family name) of your file.
The second thing you have to do is accumulate a total of each type of meal - keeping in mind that the data you are reading is a string, and not an integer that you can add, so you'll have to do some conversion.
To put all that together, try this snippet:
import csv
d = dict()
with open(r'd:/file.csv') as f:
reader = csv.reader(f)
for row in reader:
# if the family name doesn't
# exist in our dictionary,
# set it with a default value of a blank dictionary
if row[2] not in d:
d[row[2]] = dict()
# If the meal type doesn't exist for this
# family, set it up as a key in their dictionary
# and set the value to int value of the count
if row[1] not in d[row[2]]:
d[row[2]][row[1]] = int(row[0])
else:
# Both the family and the meal already
# exist in the dictionary, so just add the
# count to the total
d[row[2]][row[1]] += int(row[0])
Once you run through that loop, d looks like this:
{'Johnsons': {'Chicken': 3, 'Steak': 2, 'Veggie': 1},
'Martins': {'Fish': 2, 'Steak': 1},
'Smiths': {'Veggie': 4}}
Now its just a matter of printing it out:
for family,data in d.iteritems():
print('{}'.format(family))
for meal, total in data.iteritems():
print('{} {}'.format(total, meal))
At the end of the loop, you'll have:
Johnsons
3 Chicken
2 Steak
1 Veggie
Smiths
4 Veggie
Martins
2 Fish
1 Steak
You can later improve this snippet by using defaultdict
First time replier so here's a go. Have you considered keeping track of the orders and then writing to a file? I tried something using a dict based approach and it seems to work fine. The idea was to index by the family name and store a list of pairs containing the order quantities and types.
You may also want to consider the readability of your code - it's hard to follow and debug. However, what I think is happening is the line
for line in reader_sorting:
Iterates through reader_sorting. You read the 1st name, extract the family name, and later proceed to iterate again in reader_sorting. This time you start at the 2nd line, which family name matches, and you print it successfully. The rest of the line don't match, but you still iterate through them all. Now you've finished iterating through reader_sorting, and the loop finishes, even though in the outer loop you've read only one line.
One solution may be to create another iterator in the outer for loop and not expend the iterator that loop goes through. However, then you still need to deal with the possibility of double counting, or keeping track of indices. Another way may be to keep of the orders by family as you iterate.
import csv
orders = {}
with open('needs_sorting.csv') as file:
needs_sorting = csv.reader(file)
for amount, meal, family in needs_sorting:
if family not in orders:
orders[family] = []
orders[family].append((amount, meal))
with open('orders.txt', 'a') as file:
for family in orders:
file.write('%s\n' % family)
for amount, meal in orders[family]:
file.write('%s %s\n' % (amount, meal))

Update: Python average income reading and writing files

I was writing a code to find the average household income, and how many families are below poverty line.
this is my code so far
def povertyLevel():
inFile = open('program10.txt', 'r')
outFile = open('program10-out.txt', 'w')
outFile.write(str("%12s %12s %15s\n" % ("Account #", "Income", "Members")))
lineRead = inFile.readline() # Read first record
while lineRead != '': # While there are more records
words = lineRead.split() # Split the records into substrings
acctNum = int(words[0]) # Convert first substring to integer
annualIncome = float(words[1]) # Convert second substring to float
members = int(words[2]) # Convert third substring to integer
outFile.write(str("%10d %15.2f %10d\n" % (acctNum, annualIncome, members)))
lineRead = inFile.readline() # Read next record
# Close the file.
inFile.close() # Close file
Call the main function.
povertyLevel()
I am trying to find the average of annualIncome and what i tried to do was
avgIncome = (sum(annualIncome)/len(annualIncome))
outFile.write(avgIncome)
i did this inside the while lineRead. however it gave me an error saying
avgIncome = (sum(annualIncome)/len(annualIncome))
TypeError: 'float' object is not iterable
currently i am trying to find which household that exceeds the average income.
avgIncome expects a sequence (such as a list) (Thanks for the correction, Magenta Nova.), but its argument annualIncome is a float:
annualIncome = float(words[1])
It seems to me you want to build up a list:
allIncomes = []
while lineRead != '':
...
allIncomes.append(annualIncome)
averageInc = avgIncome(allIncomes)
(Note that I have one less indentation level for the avgIncome call.)
Also, once you get this working, I highly recommend a trip over to https://codereview.stackexchange.com/. You could get a lot of feedback on ways to improve this.
Edit:
In light of your edits, my advice still stands. You need to first compute the average before you can do comparisons. Once you have the average, you will need to loop over the data again to compare each income. Note: I advise saving the data somehow for the second loop, instead of reparsing the file. (You may even wish to separate reading the data from computing the average entirely.) That might best be accomplished with a new object or a namedtuple or a dict.
sum() and len() both take as their arguments an iterable. read the python documentation for more on iterables. you are passing a float into them as an argument. what would it mean to get the sum, or the length, of a floating point number? even thinking outside the world of coding, it's hard to make sense of that.
it seems like you need to review the basics of python types.

Categories