Choosing random words from a file without duplicates Python (sets) - python

I'm attempting to create a program which selects 10 words from a text file which contains 10+ words. For the purpose of the program when importing these 10 words from the text file, I must not import the same words twice! Currently I'm utilising a set for this however I'm greeted by a syntax error. I have some knowledge of sets and know they cannot hold the same value twice. As of now I'm clueless on how to solve this any help would be much appreciated. THANKS!
Relevent Code: (FileSelection)= open file dialog
def GameStage03_E():
global WordSet
if WrdCount >= 10:
WordSet = set()
for n in range(0,10):
FileLines = open(FileSelection).read().splitlines()
RandWrd = random.choice(FileLines)
WordSet.update(set([RandWrd]))
SelectButton.destroy()
GameStage01Button.destroy()
GameStage04_E()
elif WrdCount <= 10:
tkinter.messagebox.showinfo("ERROR", " Insufficient Amount Of Words Within Your Text File! ")
error code:
File "C:\Python34\lib\random.py", line 256, in choice
return seq[i]
`TypeError: 'set' object does not support indexing`

You can just use random.sample (2/3), so you don't have to do that yourself. You also don't need the call to list bigblind's answer suggests, because random.sample can take a set as an argument:
WordSet.update(random.sample(FileLines, 10))
That way, you can replace the entire body of that function with this:
try:
WordSet.update(random.sample(FileLines, 10))
except ValueError:
stkinter.messagebox.showinfo("ERROR", "The text file doesn't have enough words!")
I also left out that global statement, which you don't need. It's only necessary if you're assigning a new value to the variable, but all you need to do is call one of its functions, update.

This happens because random.choiceis trying to access the set as if it is a list (or some other datastructure that implements __getitem__). To solve this, change your call to random.choice to:
random.choice(list(FileLines))
This converts the set to a list before passing it to random.choice.

You can just use random.sample(the_list, 10) to get 10 distinct elements instead of repeatedly trying to add to a set using a loop.

Related

Python list(?) sending incorrect number of values to other program

I have been trying to upload close to a thousand SVG files to a particular program (FontForge) and as such searched for a way to automate it. Unfortunately I am really unfamiliar with Python, to the extent that I'm not sure if what I changed about the code ended up changing something fundamental.
The original code you were meant to individually continue the table that the original coder left with the file name and glyph names of the SVG files. This would require doing it manually, as I realized quickly it wouldn't allow loops within the brackets itself. The original code was as follows, albeit with more items:
select = [
('null', 'null'),
('bs-info-circle', 'infoCircle'),
]
Looking at it, and with a lot of googling and experimentation, I guessed that it was a list of tuples. As such, I created various loops adding onto a toSelect list that I created. Since they are rougly the same I'll just show one here:
for x in svc:
icon="svc-"+x+"con_lang"
glyph=x
#print((icon, glyph))
toSelect.extend(((icon, glyph),)) #comma necessary to force it to add in a pair, rather than individually
The variable svc is a list of strings: ['ba', 'be', 'bi', 'bo'...] pulled from a TXT file. The variable toSelect, when printed, looks as follows:
[('svc-bacon_lang', 'ba'), ('svc-becon_lang', 'be'), ('svc-bicon_lang', 'bi'), ...]
Long story short, I now have a list that seems to be formatted the same as the contents of the original code. As such, I set it equal in a simple manner:
select = toSelect
However, running the build program that pulls from this code is giving the following error message:
Traceback (most recent call last):
File "C:\Users\*****\Downloads\ff-batch-main\ff-batch-main\build.py", line 392, in <module>
run_fontforge(config)
File "C:\Users\*****\Downloads\ff-batch-main\ff-batch-main\build.py", line 148, in run_fontforge
for key, name in config.select:
ValueError: not enough values to unpack (expected 2, got 1)
I have tried every variation of declaring select that I can, including a few that cause the following error message:
Traceback (most recent call last):
File "C:\Users\*****\Downloads\ff-batch-main\ff-batch-main\build.py", line 392, in <module>
run_fontforge(config)
File "C:\Users\*****\Downloads\ff-batch-main\ff-batch-main\build.py", line 148, in run_fontforge
for key, name in config.select:
ValueError: too many values to unpack (expected 2)
Printing the value of select[0] does seem to tell me that that error is caused by all of the entries being considered one entry on the list? So at least I know that.
Still, I can't figure out why it doesn't take my original attempt, as select[0] = ('null', 'null'). I'm worried that select isn't supposed to be a list at all, but is something different that I'm simply unfamiliar with since I do not know python. Is this some sort of function that I broke by adding items sequentially instead of all at once?
I will also show the code from the 'build' program that is flagged as the problem. The only thing that I edited was the 'config' program, as instructed by the coder, but hopefully this at least will give context?
def run_fontforge(config):
from icon_map import maps
try:
from select_cache import maps as icons
except:
icons = {}
print(f"Generating fonts => {config.build_dir}/{config.font_name}.ttf")
with open('select_cache.py', 'w') as f:
f.write("# SVG to Font Icon mapping\n")
f.write(f"# Generated: {datetime.now()}\n")
f.write("maps = {\n")
last = config.font_start_code - 1
for _,m in icons.items():
last = max(last, m['code'])
last += 1
for key, name in config.select:
icon = icons.get(key)
src = maps.get(key)
So yeah. Um, any advice or explanations would be greatly appreciated, and I'll do my best to give additional information when possible? Unfortunately I started trying to understand Python yesterday and am coming at this from a rusty knowledge of java so I am not really fluent, might not know terms and whatnot. I just wanted to import some files man...
You've gotten quite far just 1 day into Python.
My hypothesis for the bug is config.select not containing the toSelect data.
Ways to investigate:
Interactively run this to verify that toSelect is a list of pairs:
for k, n in toSelect: pass
print both variables, or interactively evaluate config.select == toSelect to compare them, or set breakpoints in the PyCharm or VSCode debugger and examine these variables.
Is this some sort of function that I broke by adding items sequentially instead of all at once?
No.
BTW, you can make:
toSelect.extend(((icon, glyph),))
easier to understand by writing it as:
toSelect.append((icon, glyph))
Bonus: The most "Pythonic" way to write that for loop is as a list comprehension:
toSelect = [("svc-"+x+"con_lang", x) for x in svc]

Loop through values of one variable to populate another variable - SPSS

I currently have the below syntax -
BEGIN PROGRAM.
import spss,spssdata
varlist = [element[0] for element in spssdata.spssdata('CARD_2_Q2_1_a').fetchall()]
varstring = " ".join(str(int(i)) for i in varlist)
spss.submit("if (Q4_2 = 2 AND CARD_2_Q2_1_a = %(varstring)s) Q4_2_FULL = %(varstring)s." %locals())
END PROGRAM.
I thought this would just loop through the values in my variable CARD_2_Q2_1_a and populate Q4_2_FULL where appropriate. It worked in long hand without Python use, but the code above doesn't change the input file at all. Any reason why this might not be working or an alternative way of doing this?
varstring will be a string of integers joined by blanks. Therefore, your test condition in the IF will never be satisfied. Hence Q4_2_FULL will never be populated. You can print out the command you are submitting to see this.
I'm not sure exactly what your desired result is, but remember that the IF command you are submitting will execute over the entire dataset.

Python - Searching a dictionary for strings

Basically, I have a troubleshooting program, which, I want the user to enter their input. Then, I take this input and split the words into separate strings. After that, I want to create a dictionary from the contents of a .CSV file, with the key as recognisable keywords and the second column as solutions. Finally, I want to check if any of the strings from the split users input are in the dictionary key, print the solution.
However, the problem I am facing is that I can do what I have stated above, however, it loops through and if my input was 'My phone is wet', and 'wet' was a recognisable keyword, it would go through and say 'Not recognised', 'Not recognised', 'Not recognised', then finally it would print the solution. It says not recognised so many times because the strings 'My', 'phone' and 'is' are not recognised.
So how do I test if a users split input is in my dictionary without it outputting 'Not recognised' etc..
Sorry if this was unclear, I'm quite confused by the whole matter.
Code:
import csv, easygui as eg
KeywordsCSV = dict(csv.reader(open('Keywords and Solutions.csv')))
Problem = eg.enterbox('Please enter your problem: ', 'Troubleshooting').lower().split()
for Problems, Solutions in (KeywordsCSV.items()):
pass
Note, I have the pass there, because this is the part I need help on.
My CSV file consists of:
problemKeyword | solution
For example;
wet Put the phone in a bowl of rice.
Your code reads like some ugly code golf. Let's clean it up before we look at how to solve the problem
import easygui as eg
import csv
# # KeywordsCSV = dict(csv.reader(open('Keywords and Solutions.csv')))
# why are you nesting THREE function calls? That's awful. Don't do that.
# KeywordsCSV should be named something different, too. `problems` is probably fine.
with open("Keywords and Solutions.csv") as f:
reader = csv.reader(f)
problems = dict(reader)
problem = eg.enterbox('Please enter your problem: ', 'Troubleshooting').lower().split()
# this one's not bad, but I lowercased your `Problem` because capital-case
# words are idiomatically class names. Chaining this many functions together isn't
# ideal, but for this one-shot case it's not awful.
Let's break a second here and notice that I changed something on literally every line of your code. Take time to familiarize yourself with PEP8 when you can! It will drastically improve any code you write in Python.
Anyway, once you've got a problems dict, and a problem that should be a KEY in that dict, you can do:
if problem in problems:
solution = problems[problem]
or even using the default return of dict.get:
solution = problems.get(problem)
# if KeyError: solution is None
If you wanted to loop this, you could do something like:
while True:
problem = eg.enterbox(...) # as above
solution = problems.get(problem)
if solution is None:
# invalid problem, warn the user
else:
# display the solution? Do whatever it is you're doing with it and...
break
Just have a boolean and an if after the loop that only runs if none of the words in the sentence were recognized.
I think you might be able to use something like:
for word in Problem:
if KeywordsCSV.has_key(word):
KeywordsCSV.get(word)
or the list comprehension:
[KeywordsCSV.get(word) for word in Problem if KeywordsCSV.has_key(word)]

Python 3 and mpg321 formating

Im having some problems playing a random sound using mpg321.
First I make a list of all the sounds and also store the length in a variable I then create a random number between 0 and the length of that list. My problem is I do not know how to add that to the string inside os.system() for the file path.
sounds = os.listdir('./sounds/') # creates list of all sound names
totalSounds = len(sounds)
sound_number = random.randint(0, len(sounds))
next_sound = str(sounds[sound_number])
soundPlaying = True
os.system('mpg321 ./sounds/%s') % next_sound
soundPlaying = False
I have tried using %s and putting the variable in after ./sounds/ but I get a syntax error saying os.system() only takes one argument.
Any help is appreciated.
The problem is that you need to do string formatting on the string, not on the function call
os.system('mpg321 ./sounds/%s'%next_sound)
By the way, I would use subprocess, which provides a much more handsome API than os.system! (https://docs.python.org/3/library/subprocess.html#subprocess.call)
import subprocess
subprocess.call(["mpg321", "./sounds/%s" % next_sound])

Extracting specific variables of a line with linecache

I'm currently using the python linecache module to grab specific lines from a given text document and create a new file with said line. For example, part of the code looks like:
cs = linecache.getline('variables.txt', 7)
cs_1 = open("lo_cs", "w")
cs_1.write(str(cs))
cs_1.close()
The problem is that within variables.txt, line 7 is given by:
variable7 = 3423
for instance. I want the new file, lo_cs, however, to contain only the actual value '3423' and not the entire line of text. Further, I want to insert the whole 'getline' command into an if loop so that if variable7 is left blank, another action is taken. Is there a way to use linecache to check the space following 'variable7 = ' to see if there is anything entered there, and if so, to grab only that particular value or string?
I know (but don't really understand) that bash scripts seem to use '$' as sort of a placeholder for inserting or calling a given file. I think I need to implement something similar to that...
I thought about having instructions in the text file indicating that the value should be specified in the line below -- to avoid selecting out only segments of a line -- but that allows for one to accidentally enter in superfluous breaks, which would mess up all subsequent 'getline' commands, in terms of which line needs to be selected.
Any help in the right direction would be greatly appreciated!
You can use the following method to wrap the functionality you need:
def parseline(l):
sp = l.split('=')
return sp[0], int(sp[1]) if len(sp) > 1 else None
or if you don't need the variable name:
def parseline(l):
sp = l.split('=')
return int(sp[1]) if len(sp) > 1 and sp[1].strip() != '' else None
and then use:
csval = parseline(linecache.getline('variables.txt', 7))
You can later place conditions on csval to see if it's None, and if it is, take another action.

Categories