Im trying to read a file and make sure that each value is in order. I dont think im converting the string into the integer correctly. Here is some of my code. I am also trying to use flags.
fileName = input("What file name? ")
infile = open(fileName,'r')
correct_order_flag = False
i = 0
line = infile.readline()
while line !="":
for xStr in line.split(" "):
if eval(xStr) [i] < i:
correct_order_flag = True
else:
correct_order_flag = False
i = i+1
if correct_order_flag:
print("Yes, the numbers were in order")
else:
print("No, the numbers were not in order")
count = i - 1
print("There were", count, "numbers.")
You are correct - you are indicating with eval(xStr)[i] that eval(xStr) is an array, and thus can be subscripted. What it looks like you may want (since you say you want to convert the string to an int) is just int(xStr), to make that whole line:
if int(xStr) < i:
For starters, you don't read the whole file at all. Try this:
with open(fileName) as f:
for line in f:
# here goes your code
Not sure though, what do you mean by "each value is in order", but using eval() is a VERY bad idea for any purpose.
I would like to add that because you are comparing xstr[i] to i that unless your first number is less than zero the flag will change, meaning that the sequence 1 2 3 4 5 would print out saying "NO, the numbers were not in order"
As Chris indicated, int(s) is the preferred way to convert a string to an integer. eval(s) is too broad and can be a security risk when evaluating data from an untrusted source.
In addition, there is another error in the script. The *correct_order_flag* is being set on every iteration, so one entry with incorrect order can be masked by a subsequent entry in the correct order. Accordingly, you should break out of the loop when incorrect ordering is found.
Related
So I am currently preparing for a competition (Australian Informatics Olympiad) and in the training hub, there is a problem in AIO 2018 intermediate called Castle Cavalry. I finished it:
input = open("cavalryin.txt").read()
output = open("cavalryout.txt", "w")
squad = input.split()
total = squad[0]
squad.remove(squad[0])
squad_sizes = squad.copy()
squad_sizes = list(set(squad))
yn = []
for i in range(len(squad_sizes)):
n = squad.count(squad_sizes[i])
if int(squad_sizes[i]) == 1 and int(n) == int(total):
yn.append(1)
elif int(n) == int(squad_sizes[i]):
yn.append(1)
elif int(n) != int(squad_sizes[i]):
yn.append(2)
ynn = list(set(yn))
if len(ynn) == 1 and int(ynn[0]) == 1:
output.write("YES")
else:
output.write("NO")
output.close()
I submitted this code and I didn't pass because it was too slow, at 1.952secs. The time limit is 1.000 secs. I wasn't sure how I would shorten this, as to me it looks fine. PLEASE keep in mind I am still learning, and I am only an amateur. I started coding only this year, so if the answer is quite obvious, sorry for wasting your time 😅.
Thank you for helping me out!
One performance issue is calling int() over and over on the same entity, or on things that are already int:
if int(squad_sizes[i]) == 1 and int(n) == int(total):
elif int(n) == int(squad_sizes[i]):
elif int(n) != int(squad_sizes[i]):
if len(ynn) == 1 and int(ynn[0]) == 1:
But the real problem is your code doesn't work. And making it faster won't change that. Consider the input:
4
2
2
2
2
Your code will output "NO" (with missing newline) despite it being a valid configuration. This is due to your collapsing the squad sizes using set() early in your code. You've thrown away vital information and are only really testing a subset of the data. For comparison, here's my complete rewrite that I believe handles the input correctly:
with open("cavalryin.txt") as input_file:
string = input_file.read()
total, *squad_sizes = map(int, string.split())
success = True
while squad_sizes:
squad_size = squad_sizes.pop()
for _ in range(1, squad_size):
try:
squad_sizes.remove(squad_size) # eliminate n - 1 others like me
except ValueError:
success = False
break
else: # no break
continue
break
with open("cavalryout.txt", "w") as output_file:
print("YES" if success else "NO", file=output_file)
Note that I convert all the input to int early on so I don't have to consider that issue again. I don't know whether this will meet AIO's timing constraints.
I can see some things in there that might be inefficient, but the best way to optimize code is to profile it: run it with a profiler and sample data.
You can easily waste time trying to speed up parts that don't need it without having much effect. Read up on the cProfile module in the standard library to see how to do this and interpret the output. A profiling tutorial is probably too long to reproduce here.
My suggestions, without profiling,
squad.remove(squad[0])
Removing the start of a big list is slow, because the rest of the list has to be copied as it is shifted down. (Removing the end of the list is faster, because lists are typically backed by arrays that are overallocated (more slots than elements) anyway, to make .append()s fast, so it only has to decrease the length and can keep the same array.
It would be better to set this to a dummy value and remove it when you convert it to a set (sets are backed by hash tables, so removals are fast), e.g.
dummy = object()
squad[0] = dummy # len() didn't change. No shifting required.
...
squad_sizes = set(squad)
squad_sizes.remove(dummy) # Fast lookup by hash code.
Since we know these will all be strings, you can just use None instead of a dummy object, but the above technique works even when your list might contain Nones.
squad_sizes = squad.copy()
This line isn't required; it's just doing extra work. The set() already makes a shallow copy.
n = squad.count(squad_sizes[i])
This line might be the real bottleneck. It's effectively a loop inside a loop, so it basically has to scan the whole list for each outer loop. Consider using collections.Counter for this task instead. You generate the count table once outside the loop, and then just look up the numbers for each string.
You can also avoid generating the set altogether if you do this. Just use the Counter object's keys for your set.
Another point unrelated to performance. It's unpythonic to use indexes like [i] when you don't need them. A for loop can get elements from an iterable and assign them to variables in one step:
from collections import Counter
...
count_table = Counter(squad)
for squad_size, n in count_table.items():
...
You can collect all occurences of the preferred number for each knight in a dictionary.
Then test if the number of knights with a given preferred number is divisible by that number.
with open('cavalryin.txt', 'r') as f:
lines = f.readlines()
# convert to int
list_int = [int(a) for a in lines]
#initialise counting dictionary: key: preferred number, item: empty list to collect all knights with preferred number.
collect_dict = {a:[] for a in range(1,1+max(list_int[1:]))}
print(collect_dict)
# loop though list, ignoring first entry.
for a in list_int[1:]:
collect_dict[a].append(a)
# initialise output
out='YES'
for key, item in collect_dict.items():
# check number of items with preference for number is divisilbe
# by that number
if item: # if list has entries:
if (len(item) % key) > 0:
out='NO'
break
with open('cavalryout.txt', 'w') as f:
f.write(out)
I am studying "Python for Everybody" book written by Charles R. Severance and I have a question to the exercise2 from Chapter7.
The task is to go through the mbox-short.txt file and "When you encounter a line that starts with “X-DSPAM-Confidence:” pull apart the line to extract the floating-point number on the line. Count these lines and then compute the total of the spam confidence values from these lines. When you reach the end of the file, print out the average spam confidence."
Here is my way of doing this task:
fname = input('Enter the file name: ')
try:
fhand = open(fname)
except:
print('File cannot be opened:', fname)
exit()
count = 0
values = list()
for line in fhand:
if line.startswith('X-DSPAM-Confidence:'):
string = line
count = count + 1
colpos = string.find(":")
portion = string[colpos+1:]
portion = float(portion)
values.append(portion)
print('Average spam confidence:', sum(values)/count)
I know this code works because I get the same result as in the book, however, I think this code can be simpler. The reason I think so is because I used a list in this code (declared it and then stored values in it). However, "Lists" is the next topic in the book and when solving this task I didn't know anything about lists and had to google them. I solved this task this way, because this is what I'd do in the R language (which I am already quite familiar with), I'd make a vector in which I'd store the values from my iteration.
So my question is: Can this code be simplified? Can I do the same task without using list? If yes, how can I do it?
I could change the "values" object to a floating type. The overhead of a list is not really needed in the problem.
values = 0.0
Then in the loop use
values += portion
Otherwise, there really is not a simpler way as this problem has tasks and you must meet all of the tasks in order to solve it.
Open File
Check For Error
Loop Through Lines
Find certain lines
Total up said lines
Print average
If you can do it in 3 lines of code great but that doesn't make what goes on in the background necessarily simpler. It will also probably look ugly.
You could filter the file's lines before the loop, then you can collapse the other variables into one, and get the values using list-comprehension. From that, you have your count from the length of that list.
interesting_lines = (line.startswith('X-DSPAM-Confidence:') for line in fhand)
values = [float(line[(line.find(":")+1):]) for line in interesting_lines]
count = len(values)
Can I do the same task without using list?
If the output needs to be an average, yes, you can accumlate the sum and the count as their own variables, and not need a list to call sum(values) against
Note that open(fname) is giving you an iterable collection anyway, and you're looping over the "list of lines" in the file.
List-comprehensions can often replace for-loops that add to a list:
fname = input('Enter the file name: ')
try:
fhand = open(fname)
except:
print('File cannot be opened:', fname)
exit()
values = [float(l[l.find(":")+1:]) for l in fhand if l.startswith('X-DSPAM-Confidence:')]
print('Average spam confidence:', sum(values)/len(values))
The inner part is simply your code combined, so perhaps less readable.
EDIT: Without using lists, it can be done with "reduce":
from functools import reduce
fname = input('Enter the file name: ')
try:
fhand = open(fname)
except:
print('File cannot be opened:', fname)
exit()
sum, count = reduce(lambda acc, l: (acc[0] + float(l[l.find(":")+1:]), acc[1]+1) if l.startswith('X-DSPAM-Confidence:') else acc, fhand, (0,0))
print('Average spam confidence:', sum / count)
Reduce is often called "fold" in other languages, and it basically allows you to iterate over a collection with an "accumulator". Here, I iterate the collection with an accumulator which is a tuple of (sum, count). With each item, we add to the sum and increment the count. See Reduce documentation.
All this being said, "simplify" does not necessarily mean as little code as possible, so I would stick with your own code if you're not comfortable with these shorthand notations.
I am trying to solve some problems in CodeAbbey using Python.I have run into a wall trying to take input for these programs.I have spent so much time analyzing how to take the input data without even solving the question.Hope someone explains how to take input.
Problem:I have to input the following numbers in One go. I have tried using 'input()' but it takes only one line. Is there any work around to do it in a simple way? i wasted so much time trying to analyse various options
632765 235464
985085 255238
621913 476248
312397 751031
894568 239825
702556 754421
474681 144592
You can find the exact question here: http://www.codeabbey.com/index/task_view/sums-in-loop
You can just repeat input() until you get all your data, e.g.:
try:
input = raw_input # fix for Python 2.x compatibility
except NameError:
pass
def input_pairs(count):
pairs = [] # list to populate with user input
print("Please input {} number pairs separated by space on each new line:".format(count))
while count > 0: # repeat until we get the `count` number of pairs
success = True # marks if the input was successful or not
try:
candidate = input() # get the input from user
if candidate: # if there was some input...
# split the input by whitespace (default for `split()`) and convert
# the pairs to integers. If you need floats you can use float(x) instead
current_pair = [int(x) for x in candidate.split()]
if len(current_pair) == 2: # if the input is a valid pair
pairs.append(current_pair) # add the pair to our `pairs` list
else:
success = False # input wasn't a pair, mark it as unsuccessful
else:
success = False # there wasn't any input, mark it as unsuccessful
except (EOFError, ValueError, TypeError): # if any of the conversions above fail
success = False # mark the input as unsuccessful
if success: # if the input was successful...
count -= 1 # reduce the count of inputs by one
else: # otherwise...
print("Invalid input, try again.") # print error
return pairs # return our populated list of pairs
Then you can call it whenever you need number pairs like:
my_pairs = input_pairs(7) # gets you a list of pairs (lists) entered by user
My first attempt would be to try a typing like "632765 235464\n985085 255238[...]" so you could read it as one line. This would be pretty hacky and not a good idea if its real userinput.
The other idea: Why not taking the input line by line and putting these lines in a list / appending them to a string?
EDIT:
I found some Code on SO, but its python2.7 i guess. ->Here
The Python3.X style would be:
#!/usr/bin/python
input_list = []
while True: # Infinite loop, left by userinput
input_str = input(">") #The beginning of each line.
if input_str == ".": #Userinput ends with the . as input
break # Leave the infinite loop
else:
input_list.append(input_str) #userinput added to list
for line in input_list: #print the input to stdout
print(line)
Hope this will help :)
I am new to Python, so I apologize if this is a simple fix. I have been stuck on a Codeval problem (Happy Numbers) for quite some time and I am not sure what is going wrong.
Problem Description:
Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1, or it loops endlessly in a cycle which does not include 1.Those numbers for which this process ends in 1 are happy, while those that do not end in 1 are unhappy.
For example:
7 is a happy number (7->49->97->130->10->1)
22 is not a happy number (22->8->64->52->29->85->89->145->42->20->4->16->37->58->89 ...)
My test input and expected outcome:
1 --> 1
7 --> 1
22 --> 0
If the number is a happy number, print out 1. If not, print out 0.
Here is the full Traceback:
Traceback (most recent call last):
File "/happy_number.py", line 58, in <module>
happy_number_check("happy_numbers.txt")
File "/happy_number.py", line 55, in happy_number_check
happy_or_not(line)
File "/happy_number.py", line 33, in happy_or_not
i = int(i)
ValueError: invalid literal for int() with base 10: ''
Here is my code:
# test = 7
def happy_or_not(number):
number = str(number)
if number == 1:
print 1
else:
new_num = 0
for i in number:
i = int(i)
if i == " ":
continue
else:
new_num += i**2
if new_num == 10 or new_num == 10:
print 1
else:
try:
happy_or_not(new_num)
except RuntimeError:
print 0
# happy_or_not(test)
def happy_number_check(file):
f = open(file, 'r+')
for line in f:
if line == "":
continue
else:
happy_or_not(line)
happy_number_check("happy_numbers.txt")
What I have already tried:
Based on what I gathered from other similar questions, the issue may be that I am not able to convert a str into an int when I hit the line i = int(i). It is my understanding that I have to convert the str type into an int type before doing any math on it, yet it looks like that is where it is failing.
I tested the happy_or_not function by itself, and it does print out the value that I expect it to. It seems like to me that the issue comes when I try and call that happy_or_not function inside of the happy_number_check function, which is reading my txt file (containing a list of numbers to test). I must not be grasping a larger principle here, so any explanations would be helpful.
This is also my first real attempt at a recursive function and there probably is a better way to structure this, so any suggestions on how to change things up to be more effective is most welcome.
Thank you in advance!
Try changing happy_number_check like this (validate each line is an integer):
def happy_number_check(file):
with open(file, 'r+') as f: # Safer way to open a file. Will automatically close for you even if something goes wrong
for line in f:
if line.strip().isdigit():
happy_or_not(line.strip())
The strip() will also make it so that you can remove this code:
if i == " ":
continue
else:
By the way, you also have a bug in your logic. You are relying on a RuntimeError - a Stack Overflow interestingly enough :-) - to terminate a test. You should really keep track of what numbers have been tried, and if you try the same number again, return 0.
Don't click this link if you don't want a straight up solution to the problem, but if you do, here is an iterative solution: http://rosettacode.org/wiki/Happy_numbers#Python
I'm trying to create a program that asks for a name of a file, opens the file, and determines the maximum and minimum values in the files, and also computes the average of the numbers in the file. I want to print the max and min values, and return the average number of values in the file. The file has only one number per line, which consists of many different numbers top to bottom. Here is my program so far:
def summaryStats():
fileName = input("Enter the file name: ") #asking user for input of file
file = open(fileName)
highest = 1001
lowest = 0
sum = 0
for element in file:
if element.strip() > (lowest):
lowest = element
if element.strip() < (highest):
highest = element
sum += element
average = sum/(len(file))
print("the maximum number is ") + str(highest) + " ,and the minimum is " + str(lowest)
file.close()
return average
When I run my program, it is giving me this error:
summaryStats()
Enter the file name: myFile.txt
Traceback (most recent call last):
File "/Applications/Wing101.app/Contents/MacOS/src/debug/tserver/_sandbox.py", line 1, in <module>
# Used internally for debug sandbox under external interpreter
File "/Applications/Wing101.app/Contents/MacOS/src/debug/tserver/_sandbox.py", line 8, in summaryStats
builtins.TypeError: unorderable types: str() > int()
I think I'm struggling determining which part to make a string. What do you guys think?
You are comparing two incompatible types str and int. You need a make sure you are comparing similar types. You may want to rewrite your for loop to include a call to make sure you are comparing two int values.
for element in file:
element_value = int(element.strip())
if element_value > (lowest):
lowest = element
if element_value < (highest):
highest = element_value
sum += element_value
average = sum/(len(file))
When python reads in files, it reads them in as type str for the whole line. You make the call to strip to remove surrounding white space and newline characters. You then need to parse the remaining str into the correct type (int) for comparison and manipulation.
You should read through your error messages, they are there to enlighten you on where and why your code failed to run. The error message traces where the error took place. the line
File "/Applications/Wing101.app/Contents/MacOS/src/debug/tserver/_sandbox.py", line 8, in summaryStats
Tells you to examine line 8 which is the place for the error takes place.
The next line:
builtins.TypeError: unorderable types: str() > int()
Tells you what is going wrong. A quick search through the python docs locates the description of the error. An easy way to search for advice is to look in the documentation for the language and maybe search for the entire error message. It is likely you are not the first person with this problem and that there is probably a discussion and solution advice available to figure out your specific error.
Lines like these:
if element.strip() > (lowest):
Should probably be explicitly converting to a number. Currently you're comparing a str to and int. Converting using int will take whitespace into account, where int(' 1 ') is 1
if int(element.string()) > lowest:
Also, you could do this like so:
# Assuming test.txt is a file with a number on each line.
with open('test.txt') as f:
nums = [int(x) for x in f.readlines()]
print 'Max: {0}'.format(max(nums))
print 'Min: {0}'.format(min(nums))
print 'Average: {0}'.format(sum(nums) / float(len(nums)))
when you call open(filename), you are constructing a file object. You cannot iterate through this in a for loop.
If each value is on it's own line: after creating the file object, call:
lines = file.readlines()
Then loop through those lines and convert to int:
for line in lines:
value = int(line)