Not sure what is causing index error in my code - python

I am working on a small project which is intended to read, create, and manipulate virtual to-do lists. There is a checkbox class, a function open_create_boxes to create a list of checkbox objects based on the contents of a plaintext file, and a function create_to_do_list_file to write a file which can be read by the other function.
# Define the checkbox class
class checkbox:
def __init__(self,label):
self.label = label
self.checked = False
def check(self):
self.checked = True
def read(self):
if self.checked:
return f"x | {self.label}"
if not self.checked:
return f"o | {self.label}"
def open_create_boxes(file):
# Open the to-do list
opened_list = open(f"{file}.dat", "r")
#Split the to-do list by line
parsed_line = opened_list.read().split("\n")
next_parsed = []
# Split each element in parsed list by the pipe symbol
for i in parsed_line:
next_parsed.append(i.split("|"))
to_do_list = []
# Iterates through the new list, creates checkbox object, checks to see if it is "checked" or not
for i in next_parsed:
b = checkbox(i[1])
if i[0] == "x":
b.check()
to_do_list.append(b)
return to_do_list
def create_to_do_list_file(list,label):
# Open or create a file label.dat
new_file = open(f"{label}.dat", "w")
for n in list:
new_file.write(f"\no|{n}")
create_to_do_list_file(["Task"],"filename")
open_create_boxes("filename")
Running this code gives me the error:
File "/home/genie/Desktop/checkboxes/checkboxes.py", line 41, in <module>
open_create_boxes("filename")
File "/home/genie/Desktop/checkboxes/checkboxes.py", line 28, in open_create_boxes
b = checkbox(i[1])
IndexError: list index out of range
So something is going wrong in my open_create_boxes function, where the list is coming out with <2 elements. I have re-written this code several times and get the same, or similar, errors.
Any help here? I'm a beginner, so I imagine there's an obvious fix, but I can't seem to manage.
Thanks!!

Quickly running your code and examining the created file reveals that its first line is empty. Of course you will only get one field if you attempt to split that. Why are you writing \n in front of the data?
Similarly, your code to read the file fails to account for the empty line, whether at the beginning or the end of the file.
More tangentially, you are forgetting to close the file you write, and generally overcomplicate matters.
Here is a quick refactoring of the two file-management functions.
def open_create_boxes(file):
to_do_list = []
with open(f"{file}.dat", "r") as lines:
for line in lines:
i = line.rstrip("\n").split("|")
# print("#", i)
b = checkbox(i[1])
if i[0] == "x":
b.check()
to_do_list.append(b)
return to_do_list
# Don't call your variable "list"
def create_to_do_list_file (items ,label):
with open(f"{label}.dat", "w") as new_file:
for n in items:
new_file.write(f"o|{n}\n")
As a general first tip for how to debug things, break your problem into smaller steps and add a print statement at various points to verify that the variables contain what you hope they should, or use a debugger and set a breakpoint to examine the program's state at those spots.

Related

My code is running fine, however it won't show the designated files being opened

This is my code:
def extraDataRecord(inFile):
line = inFile.readline()
if line == "":
return[]
else:
parts = line.rsplit("",1)
parts[1] = int(parts[1])
return parts
popFile = "GhostTownPop.txt"
areaFile = "GhostTownArea.txt"
denFile = "GhostTownPopDensity.txt"
def main():
open(popFile, "r")
open(areaFile, "r")
open(denFile, "w")
popData = 'extractDataRecord',(popFile)
while len(popData) == 2:
areaData = 'extractDataRecord',(areaFile)
Town = popData[0]
Population = popData[1]
Area = areaData[1]
Density = 0.0
if Area > 0:
Density = Population/Area
denFile.write("%-25s%10.2f\n" % (Town, Density))
popFile.close()
areaFile.close()
denFile.close()
main()
When I run the code, nothin shows up, but when I try to cancel it, it says it's running fine.
What should happen is that the data files of Ghost Town populations and areas and writes out to the Ghost Town population density file for each town to a file named “GhostTownPopDensity.txt”.
OK, let me see if I can point out what you did here.
Your code was not "running fine" in any way. You were in an infinite loop doing nothing. Because of the problems pointed out below, popData contains a tuple with 2 strings, thus len(popData) was always equal to 2. You were thus executing the 5 lines in that loop, over and over and over, doing nothing at all.
First, you have an indentation problem. As written, those three "open" statements were the only thing in main, and they would not have executed until all the other code was finished. Everything down to the denFile.close() should have been indented, to be part of the main function. You had the same problem in extraDataRecord; the whole else clause needs to be indented to be part of the function.
Second, saying open(popFile,"r") does nothing. The open function returns a file object. That's what you need to use in I/O statements. As it is, you're trying to use the file NAME as an I/O object.
Third, saying areaData = 'extractDataRecord',(areaFile) does not call a function. All that does is create a tuple of two strings, To call a function, you have to do extractDataRecord(areaFile), with no quotes.
Finally, you called the function extraDataRecord, but I'm pretty sure you meant extractDataRecord. I left that alone.
def extraDataRecord(line):
parts = line.rstrip().rsplit(None,1)
parts[1] = int(parts[1])
return parts
popFileName = "GhostTownPop.txt"
areaFileName = "GhostTownArea.txt"
denFileName = "GhostTownPopDensity.txt"
def main():
popFile = open(popFileName, "r")
areaFile = open(areaFileName, "r")
denFile = open(denFileName, "w")
for line in popFile:
popData = extraDataRecord(line)
areaData = extraDataRecord(areaFile.readline())
Town = popData[0]
Population = popData[1]
Area = areaData[1]
Density = 0.0
if Area > 0:
Density = Population/Area
denFile.write("%-25s%10.2f\n" % (Town, Density))
popFile.close()
areaFile.close()
denFile.close()
main()
It looks like the two input files must have the same lines in the same order, so you could simplify this somewhat by doing:
for line1,line2 in zip(popFile,areaFile):
popData = extraDataRecord(line1)
areaData = extraDataRecord(line2)
but that's a micro-optimization. If they aren't in the same order, then your code won't work. Let us know.

Copy specified number of lines to multiple documents with a Python script that can be run from the command line

I am trying to build a script that copies a specified number of lines from one document to multiple other documents. The copied lines are supposed to be appended to the end of the docs. In case I want to delete lines from the end of the docs, the script also has to be able to delete a specified number of lines.
I want to be able to run the script from the command line and want to pass two args:
"add" or "del"
number of lines (counting from the end of the document)
A command could look like this:
py doccopy.py add 2 which would copy the last 2 lines to the other docs, or:
py doccopy.py del 4 which would delete the last 4 lines from all docs.
So far, I have written a function that copies the number of lines I want from the original document,
def copy_last_lines(number_of_lines):
line_offset = [0]
offset = 0
for line in file_to_copy_from:
line_offset.append(offset)
offset += len(line)
file_to_copy_from.seek(line_offset[number_of_lines])
changedlines = file_to_copy_from.read()
a function that pastes said lines to a document
def add_to_file():
doc = open(files_to_write[file_number], "a")
doc.write("\n")
doc.write(changedlines.strip())
doc.close()
and a main function:
def main(action, number_of_lines):
if action == "add":
for files in files_to_write:
add_to_file()
elif action == "del":
for files in files_to_write:
del_from_file()
else:
print("Not a valid action.")
The main function isn't done yet, of course and I have yet to figure out how to realize the del_from_file function.
I also have problems with looping through all the documents.
My idea was to make a list including all the paths to the documents i want to write in and then loop through this list and to make a single variable for the "original" document, but I don't know if that's even possible the way I want to do it.
If possible, maybe someone has an idea for how to realize all this with a single list, have the "original" document be the first entry and loop through the list starting with "1" when writing to the other docs.
I realize that the code I've done so far is a total clusterfuck and I ask a lot of questions, so I'd be grateful for every bit of help. I'm totally new to programming, I just did a Python crash course in the last 3 days and my first own project is shaping out to be way more complicated than I thought it would be.
This should do what you ask, I think.
# ./doccopy.py add src N dst...
# Appends the last N lines of src to all of the dst files.
# ./doccopy.py del N dst...
# Removes the last N lines from all of the dst files.
import sys
def process_add(args):
# Fetch the last N lines of src.
src = argv[0]
count = int(args[1])
lines = open(src).readlines()[-count:]
# Copy to dst list.
for dst in args[2:}
open(dst,'a').write(''.join(lines))
def process_del(args):
# Delete the last N lines of each dst file.
count = int(args[0])
for dst in args[1:]:
lines = open(dst).readlines()[:-count]
open(dst,'w').write(''.join(lines))
def main():
if sys.argv[1] == 'add':
process_add( sys.argv[2:] )
elif sys.argv[1] == 'del':
process delete( sys.argv[2:] )
else:
print( "What?" )
if __name__ == "__main__":
main()

Python - How to create a delete function for user input?

I've been trying to create a program which allows users to view a text file's contents and delete some or all of a single entry block.
An example of the text's file contents can be seen below:
Special Type A Sunflower
2016-10-12 18:10:40
Asteraceae
Ingredient in Sunflower Oil
Brought to North America by Europeans
Requires fertile and moist soil
Full sun
Pine Tree
2018-12-15 13:30:45
Pinaceae
Evergreen
Tall and long-lived
Temperate climate
Tropical Sealion
2019-01-20 12:10:05
Otariidae
Found in zoos
Likes fish
Likes balls
Likes zookeepers
Big Honey Badger
2015-06-06 10:10:25
Mustelidae
Eats anything
King of the desert
As such, the entry block refers to all lines without a horizontal space.
Currently, my progress is at:
import time
import os
global o
global dataset
global database
from datetime import datetime
MyFilePath = os.getcwd()
ActualFile = "creatures.txt"
FinalFilePath = os.path.join(MyFilePath, ActualFile)
def get_dataset():
database = []
shown_info = []
with open(FinalFilePath, "r") as textfile:
sections = textfile.read().split("\n\n")
for section in sections:
lines = section.split("\n")
database.append({
"Name": lines[0],
"Date": lines[1],
"Information": lines[2:]
})
return database
def delete_creature():
dataset = get_dataset()
delete_question = str(input("Would you like to 1) delete a creature or 2) only some of its information from the dataset or 3) return to main page? Enter 1, 2 or 3: "))
if delete_question == "1":
delete_answer = str(input("Enter the name of the creature: "))
for line in textfile:
if delete_answer in line:
line.clear()
elif delete_question == "2":
delete_answer = str(input("Enter the relevant information of the creature: "))
for line in textfile:
if delete_answer in line:
line.clear()
elif delete_question == "3":
break
else:
raise ValueError
except ValueError:
print("\nPlease try again! Your entry is invalid!")
while True:
try:
option = str(input("\nGood day, This is a program to save and view creature details.\n" +
"1) View all creatures.\n" +
"2) Delete a creature.\n" +
"3) Close the program.\n" +
"Please select from the above options: "))
if option == "1":
view_all()
elif option == "2":
delete()
elif option == "3":
break
else:
print("\nPlease input one of the options 1, 2 or 3.")
except:
break
The delete_function() is meant to delete the creature by:
Name, which deletes the entire text block associated with the name
Information, which deletes only the line of information
I can't seem to get the delete_creature() function to work, however, and I am unsure of how to get it to work.
Does anyone know how to get it to work?
Many thanks!
Your problem with removing lines from a section is that you specifically hardcoded which line represents what. Removing a section in your case will be easy, removing a line will, if you do not change your concept, involve setting the line in question to empty or to some character representing the empty string later.
Another question here is, do you need your sections to remain ordered as they were entered, or you can have them sorted back in a file in some other order.
What I would do is to change the input file format to e.g. INI file format. Then you can use the configparser module to parse and edit them in an easy manner.
The INI file would look like:
[plant1]
name="Some plant's English name"
species="The plant's Latin species part"
subspecies="The plant's subspecies in Latin ofcourse"
genus="etc."
[animal1]
# Same as above for the animal
# etc. etc. etc.
configparser.ConfigParser() will let you load it in an dictionarish manner and edit sections and values. Sections you can name animal1, plant1, or use them as something else, but I prefer to keep the name inside the value, usually under the name key, then use configparser to create a normal dictionary from names, where its value is another dictionary containing key-value pairs as specified in the section. And I reverse the process when saving the results. Either manually, or using configparser again.
The other format you might consider is JSON, using the json module.
Using its function dumps() with separators and indentation set correctly, you will get pretty human-readable and editable output format. The nice thing is that you save the data structure you are working with, e.g. dictionary, then you load it and it comes back as you saved it, and you do not need to perform some additional stuff to get it done, as with configparser. The thing is, that an INI file is a bit less confusing for an user not custom to JSON to construct, and results in less errors, while JSON must be strictly formatted, and any errors in opening and closing the scopes or with separators results in whole thing not working or incorrect input. And it easily happens when the file is big.
Both formats allows users to put empty lines wherever they want and they will not change the way the file will be loaded, while your method is strict in regard to empty lines.
If you are expecting your database to be edited only by your program, then use the pickle module to do it and save yourself the mess.
Otherwise you can:
def getdata (stringfromfile):
end = {}
l = [] # lines in a section
for x in stringfromfile.strip().splitlines():
x = x.strip()
if not x: # New section encountered
end[l[0].lower()] = l[1:]
l = []
continue
end.append(x)
end[l[0].lower()] = l[1:] # Add last section
# Connect keys to numbers in the same dict(), so that users can choose by number too
for n, key in enumerate(sorted(end)):
end[n] = key
return end
# You define some constants for which line is what in a dict():
values = {"species": 0, "subspecies": 1, "genus": 2}
# You load the file and parse the data
data = getdata(f.read())
def edit (name_or_number, edit_what, new_value):
if isinstance(name_or_number, int):
key = data[name_or_number]
else:
key = name_or_number.lower().strip()
if isinstance(edit_what, str):
edit_what = values[edit_what.strip().lower()]
data[key][edit_what] = new_value.strip()
def add (name, list_of_lines):
n = len(data)/2 # Number for new entry for numeric getting
name = name.strip().lower()
data[name] = list_of_lines
data[n] = name
def remove (name):
name = name.lower().strip()
del data[name]
# Well, this part is bad and clumsy
# It would make more sense to keep numeric mappings in separate list
# which will do this automatically, especially if the database file is big-big-big...
# But I started this way, so, keeping it simple and stupid, just remap everything after removing the item (inefficient as hell itself)
for x in data.keys():
if isinstance(x, int):
del data[x]
for n, key in enumerate(sorted(data)):
data[n] = key
def getstring (d):
# Serialize for saving
end = []
for l0, ls in d.items():
if isinstance(l0, int):
continue # Skip numeric mappings
lines = l0+"\n"+"\n".join(ls)
end.append(lines)
return "\n\n".join(end)
I didn't test the code. There might be bugs.
If you need no specific lines, you can modify my code easily to search in the lines using the list.index() method, or just use numbers for the lines if they exist when you need to get to them. For doing so with configparser, use generic keys in a section like: answer0, answer1..., or just 0, 1, 2..., Then ignore them and load answers as a list or however. If you are going to use configparser to work on the file, you will get sometimes answer0, answer3... when you remove.
And a warning. If you want to keep the order in which input file gives the creatures, use ordereddict instead of the normal dictionary.
Also, editing the opened file in place is, of course, possible, but complicated and inadvisable, so just don't. Load and save back. There are very rare situations when you want to change the file directly. And for that you would use the mmap module. Just don't!

Python function doesnt work inside a loop

I am trying to create a code to compare gene file with gene panels.
The gene panel file is in csv format and has Chromosome, gene, start location and end locations.
patients file has chromosome, mutations and the location.
so i made a loop to pass gene panel information to a function where the comparison is done to return me a list of similar items.
the function works great when i call it with manual data. but doenst not do the comparison inside the loop.
import vcf
import os, sys
records = open('exampleGenePanel.csv')
read = vcf.Reader(open('examplePatientFile.vcf','r'))
#functions to find mutations in patients sequence
def findMutations(gn,chromo,start,end):
start = int(start)
end = int(end)
for each in read:
CHROM = each.CHROM
if CHROM != chromo:
continue
POS = each.POS
if POS < start:
continue
if POS > end:
continue
REF = each.REF
ALT = each.ALT
print (gn,CHROM,POS,REF,ALT)
list.append([gn,CHROM,POS,REF,ALT])
return list
gene = records.readlines()
list=[]
y = len (gene)
x=1
while x < 3:
field = gene[x].split(',')
gname = field[0]
chromo = field[1]
gstart = field[2]
gend = field[3]
findMutations(gname,chromo,gstart,gend)
x = x+1
if not list:
print ('Mutation not found')
else:
print (len(list),' Mutations found')
print (list)
i want to get the details of matching mutations in the list.
This works as expected when i pass the data manually to the function.
Eg.findMutations('TESTGene','chr8','146171437','146229161')
But doesnt compare when passed through the loop
The problem is that findMutations attempts to read from read each time it is called, but after the first call, read has already been read and there's nothing left. I suggest reading the contents of read once, before calling the function, then save the results in a list. Then findMutations can read the list each time it is called.
It would also be a good idea to use a name other than list for your result list, since that name conflicts with the Python built-in function. It would also be better to have findMutations return its result list rather than append it to a global.

I'm having trouble with input and output from files

I'm having some trouble with my code in python. I just started learning input and output in class, and how to have python read in data from text files(barely. I'm still a huge beginner). Anyways, my assignment is that I have to have my program read in data from a file and run it through my program. Problem is, I don't have a good idea on how to do that and was wondering if you guys could help me out. The text file just contains a huge life of numbers for python to use in my program. My program finds the mean, median, and standard deviation of a list of numbers that are given to it. Now, instead of user input data, my professor wants python to use data from a file that was already pre-written.
My code:
import math
def mean(values):
average = sum(values)*1.0/len(values)
return average
def deviation(values):
length = len(values)
m = mean(values)
total_sum = 0
for i in range(length):
total_sum += (values[i]-m)**2
root = total_sum*1.0/length
return math.sqrt(root)
def median(values):
if len(values)%2 != 0:
return sorted(values)[len(values)//2]
else:
midavg = (sorted(values)[len(values)//2] + sorted(values)[len(values)//2-1])//2.0
return midavg
def main():
x = [15, 17, 40, 16, 9]
print (mean(x))
print (deviation(x))
print (median(x))
main()
Now, I have to edit my code so it opens the file, takes the data, and reads the data through my equations. Only problem is, I don't have a good idea on how to do that. Could anyone please help?
I've tried basic input and output myself, but it's done no justice in helping me with the bigger picture.
def main():
total=0
input = open('Stats.txt')
for nextline in input:
mylist = nextline.split()
for n in mylist:
total+=int(n)
print(total)
You have to fill your list from the file.
Open the file and iterate over the lines. Convert the content of the line to an integer and append it to your list. If you don't cxonvert the data you'll get strings and those won't work with mathematical operations. Close your file.
Now work with your list.
filename = 'newfile.txt'
data = []
source = open(filename)
for line in source:
data.append(int(line))
source.close()
print(mean(data))
print(deviation(data))
# more stuff with data
There is a way to let Python close the file for you so you won't have to remember it.
with open(filename) as source:
for line in source:
data.append(int(line))
According to your edit this might not be what you want. If the numbers are in one line, rather than one number per line, you'll have to take a different approach (split).

Categories

Resources