Selecting Objects in List using Python - python

To get to the point, here is my input.txt file:
Variable 1,Variable 2,Variable 3,Variable 4
Test anxiety,Worry,Emotionality,28
ERS,Reppraisal,Suppression,33
Calling,Money,Love, 69
Here is my code:
class Psych:
def __init__(self, des1, des2, des3, value):
self.__des1 = des1
self.__des2 = des2
self.__des3 = des3
self.__value = value
def getDes1(self):
return str(self.__des1)
def getDes2(self):
return str(self.__des2)
def getDes3(self):
return str(self.__des3)
def getValue(self):
return round(float(self.__value),2)
#Import data
filePath='input.txt'
file=open(filePath)
data=file.readlines()
file.close()
#Remove first row
data2 = data[1:]
#Strip /n
data3 = []
for i in data2:
data3.append(i.strip())
#Pop, Split, Create and Append Psych objects into listOfVariables
listOfVariables=[]
#Variable 1
var1 = data3.pop(0)
var1 = var1.split(",")
var1 = Psych(var1[0],var1[1],var1[2],float(var1[3]))
listOfVariables.append(var1)
#Variable 2
var2 = data3.pop(0)
var2 = var2.split(",")
var2 = Psych(var2[0],var2[1],var2[2],float(var2[3]))
listOfVariables.append(var2)
#Variable 3
var3 = data3.pop(0)
var3 = var3.split(",")
var3 = Psych(var3[0],var3[1],var3[2],float(var3[3]))
listOfVariables.append(var3)
#Printing function
def printVariables(var):
print(var.getDes1())
print(var.getDes2())
print(var.getDes3())
print(var.getValue())
#Navigation Menu
print("\nVariable 1")
print("--------------")
print("Variable Details:")
printVariables(var1)
print()
usrInput=input("Press 'N' to see the next variable\nPress 'P' to see the previous variable")
Essentially, using the Navigation Menu as the default, I am struggling to write a code that prints out
Variable 2
Variable Details:
output from printVariables(var2)
when usrInput=="N" and prints out
Variable 3
Variable Details:
output from printVariables(var3)
when usrInput=="P".
Similarly, when printVariables(var2) is already executed, usrInput=="N" should print out
Variable 3
Variable Details:
output from printVariables(var3)
and usrInput=="P" should print out
Variable 1
Variable Details:
output from printVariables(var1)
Further issue:
While attempting to solve this problem, I tried
test = listOfVariables[-2]
test1 = test[0]
print(test1.getDes1())
and encountered this error: TypeError: 'Psych' object is not subscriptable
What does this error mean? Sorry I am very new to programming (as can be seen from my codes) so please give a layperson explanation
Thank you in advance

Here is a simple solution that loads the CSV file, prints out the first row of the CSV, then prompts the user for N=Next, P=Previous, or X=Exit. It cycles through the CSV rows.
import csv
FILENAME_CSV = 'input.csv'
COLUMNS = ['Variable 1', 'Variable 2', 'Variable 3', 'Variable 4']
def print_variable(variables, index):
print('Variable:', index + 1)
for col in COLUMNS:
print('\t', variables[index][col])
def load_variables(filename):
with open(filename) as csv_data:
csv_reader = csv.DictReader(csv_data)
return list(csv_reader)
index = 0
choice = ''
variables = load_variables(FILENAME_CSV)
while choice.upper() != 'X':
print_variable(variables, index)
choice = input('Press N for Next, P for Previous, X to exit: ')
if choice.upper() == 'N':
index = (index + 1) % len(variables)
elif choice.upper() == 'P':
index = (index - 1) % len(variables)

Related

How to create a function based on another dataframe column being True?

I have a dataframe shown below:
Name X Y
0 A False True
1 B True True
2 C True False
I want to create a function for example:
example_function("A") = "A is in Y"
example_function("B") = "B is in X and Y"
example_function("C") = "C is in X"
This is my code currently (incorrect and doesn't look very efficient):
def example_function(name):
for name in df['Name']:
if df['X'][name] == True and df['Y'][name] == False:
print(str(name) + "is in X")
elif df['X'][name] == False and df['Y'][name] == True:
print(str(name) + "is in Y")
else:
print(str(name) + "is in X and Y")
I eventually want to add more Boolean columns so it needs to be scalable. How can I do this? Would it be better to create a dictionary, rather than a dataframe?
Thanks!
If you really want a function you could do:
def example_function(label):
s = df.set_index('Name').loc[label]
l = s[s].index.to_list()
return f'{label} is in {" and ".join(l)}'
example_function('A')
'A is in Y'
example_function('B')
'B is in X and Y'
You can also compute all the solutions as dictionary:
s = (df.set_index('Name').replace({False: pd.NA}).stack()
.reset_index(level=0)['Name']
)
out = s.index.groupby(s)
output:
{'A': ['Y'], 'B': ['X', 'Y'], 'C': ['X']}
I think you can stay with a DataFrame, the same output can be obtained with a function like this:
def func (name, df):
# some checks to verify that the name is actually in the df
occurrences_name = np.sum(df['Name'] == name)
if occurrences_name == 0:
raise ValueError('Name not found')
elif occurrences_name > 1:
raise ValueError('More than one name found')
# get the index corresponding to the name you're looking for
# and select the corresponding row
index = df[df['Name'] == name].index[0]
row = df.drop(['Name'], axis=1).iloc[index]
outstring = '{} is in '.format(name)
for i in range(len(row)):
if row[i] == True:
if i != 0: outstring += ', '
outstring += '{}'.format(row.index[i])
return outstring
of course you can adapt this to the specific shape of your df, I'm assuming that the column containing names is actually 'Name'.

2D Array update actually updates two different arrays? [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 1 year ago.
I'm trying to track how many times a selection was made in a 2D array and how many times it was played in another array (it has to deal with having multiple mp3's in a directory). Not even sure I'm describing it well, but hopefully my example will show my issue.
If you take the code below (and don't have the "PlayHistory.csv" file created), run it, type "A1" you will see two 2D arrays print out. Specifically look at entries for "A1". The Selection Count is 0 and Allowed Selection Count is 1. This is what I'd expect. Now, close the program (or IDE in my case) the "PlayHistory.csv" file is present and notice A1 entry is 0). Run the program again and use "A1" as your input and you'll see both the Selection Count is 1 and the Allowed Selection Count is 1. I'm not even updating the SelectionCount array in the attached code. How is this happening?
#!/usr/bin/env python3
#
import os, sys, csv, vlc, time, serial
from pynput.keyboard import Key, Listener
#
USBDrive = None
Action = None
Playlist = []
SelectionCount = []
AllowedCount = []
Sel_char = None
Queue_counter = 0
Play_counter = 0
PlayHistory="PlayHistory.csv"
#
# Create/Find/Open PlayHistory.csv
# Create media folders as needed if new PlayHistory file is created
#
USBDrive = os.path.join("/media/pi", "USB30FD")
if os.path.isfile(os.path.join(USBDrive, PlayHistory)):
datafile = open(os.path.join(USBDrive, PlayHistory), 'r+')
datareader = csv.reader(datafile, delimiter=',')
for row in datareader:
SelectionCount.append(row)
AllowedCount.append(row)
else:
datafile = open(os.path.join(USBDrive, PlayHistory), 'w+')
datawriter = csv.writer(datafile, delimiter=',')
datawriter.writerow(['Selection', 'Count'])
SelectionCount.append(['Selection', 'Count'])
AllowedCount.append(['Selection', 'Count'])
for x in range (65, 87):
if x == 73 or x == 79:
continue
for y in range (1,11):
if y == 10:
datawriter.writerow([chr(x) + str(0),0])
SelectionCount.append([chr(x) + str(0),0])
AllowedCount.append([chr(x) + str(0),0])
# if not os.path.isdir(os.path.join(USBDrive, chr(x) + str(0))):
# os.makedirs(os.path.join(USBDrive, chr(x) + str(0)))
else:
datawriter.writerow([chr(x) + str(y),0])
SelectionCount.append([chr(x) + str(y),0])
AllowedCount.append([chr(x) + str(y),0])
# if not os.path.isdir(os.path.join(USBDrive, chr(x) + str(y))):
# os.makedirs(os.path.join(USBDrive, chr(x) + str(y)))
datafile.flush()
datafile.close()
#AllowedCount == SelectionCount
#
# Find location of Selection in SelectionCount 2D array
#
def find(l, elem):
for row, i in enumerate(l):
try:
column = i.index(elem)
except ValueError:
continue
return row, column
return -1
#
# MediaPlayer function
#
def QueueTracker(tracker, selection):
global Queue_counter, Play_counter
if tracker == "Queue":
Queue_counter = Queue_counter + 1
elif tracker == "Play":
print("Play Counter")
Play_counter = Play_counter + 1
count1,count2=find(SelectionCount, selection)
#SelectionCount[count1][1] = int(SelectionCount[count1][1]) + 1
print("SelectionCount: " + str(SelectionCount[count1][1]))
elif tracker == "Allowed":
print("Allowed Counter")
acount1,acount2=find(AllowedCount, selection)
print(AllowedCount[acount1][1])
AllowedCount[acount1][1] = int(AllowedCount[acount1][1]) + 1
print("AllowedSelectionCount: " + str(AllowedCount[acount1][1]))
print("Selected Count")
print(SelectionCount)
print("Allowed Selection Count")
print(AllowedCount)
#
# Define keyboard actions
#
def on_press(key):
global Action
try:
Sel_char = int(key.char)
except:
try:
Sel_char = str(key.char)
Sel_char = Sel_char.upper()
except:
Sel_char = None
if Sel_char == "Z":
return False
elif Sel_char == "Y":
print("Play Track")
QueueTracker("Played", "A1")
elif type(Sel_char) == str:
Action = Sel_char
elif type(Sel_char) == int:
Action = Action + str(Sel_char)
QueueTracker("Allowed", "A1")
else:
pass
# Read keyboard input
#
print("Ready...")
with Listener(on_press=on_press) as listener:
listener.join()
#
# Program is shutting down
#
print ("")
print ("Writing Play Counts to PlayHistory file")
datafile = open(os.path.join(USBDrive, PlayHistory), 'w+')
datawriter = csv.writer(datafile, delimiter=',')
datawriter.writerows(SelectionCount)
datafile.flush()
datafile.close()
print ("")
print ("Have a nice day!")
print ("")
sys.exit()
Any help would be greatly appreciated.
Thanks! Brian
Your assessment was essentially correct:
for row in datareader:
SelectionCount.append(row)
AllowedCount.append(row)
That does not make two copies. That makes two pointers to one list. If you modify the list in SelectionCount[0], that will also modify AllowedCount[0]. Change that to
for row in datareader:
SelectionCount.append(row)
AllowedCount.append(row[:])
You might also consider this simplification to your first loop creating the CSV data:
for x in 'ABCDEFGHJKLMNPQRSTUV':
for y in range (10):
datawriter.writerow([x + str(y),0])
SelectionCount.append([x + str(y),0])
AllowedCount.append([x + str(y),0])

Lists in classes losing data

I'm trying to parsing text from a file, the first character in a line will be used to decide of what type the remaining text should be.
'?' - Question, '#' - Description, '!' - Answers, and the next number will determine what is the correct answer.
I want to have an object that stores the respective data, and a class that holds references to those objects (list of objects). The amount of answers can vary, so I tried to use a list for that.
The problem is that the returned "answers" list is blank.
Where did I go wrong?
class Question:
def __init__(self, ques, desc, n_ans, corr, ans):
self.ques = ques
self.desc = desc
self.n_ans = n_ans
self.corr = corr
self.ans = ans
print(self.ans)
def get(self):
print(self.ans)
return [self.ques, self.desc, self.n_ans, self.corr, self.ans]
class Question_handler:
arr_ans = []
array = []
firstTime = True
def __init__(self):
self.num_ans = 0
self.cor_ans = 0
self.ques = None
self.desc = None
def question(self, ques): # if it isn't the first time reaching a line starts with '?'
if self.firstTime == True: # the data is made into an instance of Question
pass
else:
self.array.append(Question(self.ques, self.desc, self.num_ans, self.cor_ans, self.arr_ans)) # List of objects
self.ques = None # Reset everything
self.desc = None
self.num_ans = 0
self.cor_ans = 0
self.arr_ans.clear()
self.ques = ques
self.firstTime = False
def description(self, desc):
if self.desc == None:
self.desc = desc
else:
self.desc = self.desc + '\n' + desc
def answer(self, ans):
self.arr_ans.append(ans) # Append answers (line starts with '!') to list
self.num_ans = len(self.arr_ans)
def correct(self, num):
if num >= 1 and num <= len(self.arr_ans):
self.cor_ans = num
else:
self.cor_ans = 0
def end(self): # Reach EOF
if self.ques == None:
pass
else:
self.question(None) # To push the remaining data
def get(self, Nques): # Get info of Question
if Nques <= len(self.array) and Nques >= 1:
return self.array[Nques - 1].get()
QQ = Question_handler()
Qfile = open('C:/Users/Admin/Desktop/test/test.txt','r')
Qdata = Qfile.readlines()
for line in Qdata:
Qline = line[1:-1]
if line[0] == '?':
QQ.question(Qline)
continue
if line[0] == '#':
QQ.description(Qline)
continue
if line[0] == '!':
QQ.answer(Qline)
continue
if line[0].isnumeric():
QQ.correct(int(line[0]))
continue
else:
QQ.end() # EOF
print(QQ.get(1))
print(QQ.get(2))
print(QQ.get(3))
TXT file
?Cau 1
#Test so 1
!abc
!def
!ghj
!ikl
3
?Cau 2
#Test so 2
!100
!1000
!10000
2
?Question 3
!A
!B
!C
!D
!E
5
Output
['abc', 'def', 'ghj', 'ikl']
['100', '1000', '10000']
['A', 'B', 'C', 'D', 'E']
[]
['Cau 1', 'Test so 1', 4, 3, []]
[]
['Cau 2', 'Test so 2', 3, 2, []]
[]
['Question 3', None, 5, 5, []]

Write list to 2 different text files

Reading in the data text file,
Running loops to check criteria for valid and invalid student numbers.
Then trying to write 2 text files, one for the invalid student numbers and one for the valid student numbers
This far everything works accept no text files written and no data written to text files at the end
Here is the Script so far
inputList = []
outputList = []
def FileOpen(myList):
#try:
count=0
INFILE=open("data.txt", "r")
for line in INFILE:
myList.append(line.rstrip())
count+=1
INFILE.close()
return count
#except:
# print("File could not be found")
# exit()
FileOpen(inputList)
print(FileOpen(inputList),"number of lines read from file")
def AnalyseStudents(rawStudents,totalStudents):
for entry in rawStudents:
amountOfDigits = len(entry)
testOfDigits = entry.isdigit()
def inValid1(val1,val2):
answ = val1 != 8 and val2 != True
return answ
inValidResult1=(inValid1(amountOfDigits,testOfDigits))
def Valid1(val1,val2):
answ = val1 == 8 and val2 == True
return answ
validResult1=(Valid1(amountOfDigits,testOfDigits))
if inValidResult1:
print(entry, " is an INVALID student number")
elif validResult1:
a=entry[0]
b=entry[1]
c=entry[2]
d=entry[3]
e=entry[4]
f=entry[5]
g=entry[6]
h=entry[7]
sum = float((a*8)+(b*7)+(c*6)+(d*5)+(e*4)+(f*3)+(g*2)+(h*1))
result = sum%11
def inValid2 (val):
answ = val != 0
return answ
inValidResult2=(inValid2(result))
def Valid2 (val):
answ = val == 0
return answ
validResult2=(Valid2(result))
if validResult2:
print(entry, " is an VALID student number")
elif inValidResult2:
print(entry, " is an INVALID student number")
totalStudents
AnalyseStudents(inputList,outputList)
def Write(outList):
if outList == (" is an VALID student number"):
OUTFILE1=open("ValidNumbers.txt","w")
for validResult in outList:
OUTFILE1.write(validResult+"\n")
OUTFILE1.close()
elif outList == (" is an INVALID student number"):
OUTFILE2=open("InvalidNumbers.txt","w")
for inValidResults in outList:
OUTFILE2.write(inValidResults+"\n")
OUTFILE2.close()
Write(outputList)
print("See output files for more details")
Your equality statements are wrong
if outList == (" is an VALID student number"):
...
elif outList == (" is an INVALID student number"):
The outList is a list but you are comparing it to strings. Put a debug breakpoint at those if statements and decide what you want.
I'm still working my way through this, but for the first function, you can use list comprehension and a couple of other Python functions. Commentary is included.
inputList = []
outputList = []
# Change parameter to the name of the file you want to open.
# Here, we can just make the parameter optional and pass your
# text file name by default.
def FileOpen(filename="data.txt"):
# Using with keeps the open file in context until it's finished
# it will then close automatically.
with open(filename, "r") as INFILE:
# Reach all the data at once and then try to decode it.
data = INFILE.read()
try:
data = data.decode("utf-8")
# If data can't be decoded, then it doesn't have that attribute.
# Catch that exception
except AttributeError:
pass
# Python 3 has a string method called .splitlines() that makes
# splitting lines a little simpler.
myList = [line for line in data.splitlines()]
# We'll return two values: The length (or count) of the list
# and the actual list.
return len(myList), myList
# We set up two variables for intake of our count and list results.
count, myList = FileOpen()
print("{} number of lines read from file".format(count))
EDIT: I don't know what the last part of your code is aiming to do. Maybe compare student count to valid entries? Here's the middle part. I moved the function outside and made it evaluate either valid or invalid in one fell swoop.
def is_valid(entry_value):
"""Validate current entry."""
if len(entry_value) != 8 and entry_value.isnumeric():
return False
return True
def AnalyseStudents(rawStudents, totalStudents):
for entry in rawStudents:
# Our new function will return True if the entry is valid
# So we can rework our function to run through the True condition
# first.
if is_valid(entry):
entry_sum = entry[0] * 8
entry_sum += entry[1] * 7
entry_sum += entry[2] * 6
entry_sum += entry[3] * 5
entry_sum += entry[4] * 4
entry_sum += entry[5] * 3
entry_sum += entry[6] * 2
entry_sum += entry[7] * 1
if entry_sum % 11 == 0:
print(entry, " is an VALID student number")
else:
print(entry, " is an INVALID student number")
else:
print(entry, " is an INVALID student number")
AnalyseStudents(inputList, outputList)

local variable 'moodsc' referenced before assignment

In the code below I get the following error:
"local variable 'moodsc' referenced before assignment"
I'm new to programming and python. I'm struggling with interpreting other questions on the similar topic. Any context around this specific code would be helpful.
import re
import json
import sys
def moodScore(sent, myTweets):
scores = {} # initialize an empty dictionary
new_mdsc = {} # intitalize an empty dictionary
txt = {}
for line in sent:
term, score = line.split("\t") # The file is tab-delimited. "\t" means "tab character"
scores[term] = int(score) # Convert the score to an integer.
data = [] # initialize an empty list
for line in myTweets:
tweet = json.loads(line)
if "text" in tweet and "lang" in tweet and tweet["lang"] == "en":
clean = re.compile("\W+")
clean_txt = clean.sub(" ", tweet["text"]).strip()
line = clean_txt.lower().split()
moodsc = 0
pos = 0
neg = 0
count = 1
for word in range(0, len(line)):
if line[word] in scores:
txt[word] = int(scores[line[word]])
else:
txt[word] = int(0)
moodsc += txt[word]
print txt
if any(v > 0 for v in txt.values()):
pos = 1
if any(v < 0 for v in txt.values()):
neg = 1
for word in range(0, len(line)): # score each word in line
if line[word] not in scores:
if str(line[word]) in new_mdsc.keys():
moodsc2 = new_mdsc[str(line[word])][0] + moodsc
pos2 = new_mdsc[str(line[word])][1] + pos
neg2 = new_mdsc[str(line[word])][2] + neg
count2 = new_mdsc[str(line[word])][3] + count
new_mdsc[str(line[word])] = [moodsc2, pos2, neg2, count2]
else:
new_mdsc[str(line[word])] = [moodsc, pos, neg, count]
def new_dict():
for val in new_mdsc.values():
comp = val[0] / val[3]
val.append(comp)
for key, val in new_mdsc.items():
print (key, val[4])
def main():
sent_file = open(sys.argv[1])
tweet_file = open(sys.argv[2])
moodScore(sent_file, tweet_file)
# new_dict()
if __name__ == '__main__':
main()
Ok #joshp, I think you need to globalise some variables, because the error is 'moodsc referenced before assignment', I think the code only gets as far as moodsc += txt[word] but you may also have trouble with pos and neg.
Try global moodsc and pos etc. before you define moodsc and pos etc. If this doesn't work try global moodsc before moodsc += txt[word] and so forth, you may need to use global in both places for it to work, I often find that this is needed in my code, to globalise it at definition and wherever else you use it (at the start of each function and statement where it is used).

Categories