My first project from scratch ! ... except I'm stuck :'(

Hello guys ! I’ve been coding a project on my own about statistics. I used OOP (or I think it is). It is about calculate statistics with some data that are all provided by users (that cannot understand code or only a little). I have a huge bug in the graph and table (and maybe others, but I didn’t see it) section : it works fine if I print it by hand, but it crashes when I use the user system. It seems the problem comes from the key turning from it initial state to ‘O’, but I don’t see any fix right now. I know that the common point is the self.dict[value] use but I don’t see any efficient way to replace it (and also it is intended to be a user-provided value, so…) Could you help me please ?

class Stats() :
    def __init__(self, dicts: dict, name):
        self.name = name
        self.dict = dicts
        self.effectif = sum(self.dict.values())


    #_______METHODS__________
    def last(self, list):
        return list[len(list) - 1]

    def odd_or_even(self, number):
        odd = [1, 3, 5, 7, 9]
        even = [0, 2, 4, 6, 8]
        for a in str(number) :
            num = int(a)
            if num in odd :
                return 1
            elif num in even :
                return 0

    def get_frequence(self, value):
        if not value in self.dict.keys() :
            return f"La variable {value} n'existe pas."
        effective = self.dict[value]
        result = int((effective / self.get_effectif)*100) if ((effective / self.get_effectif)*100) % 1 == 0 else (effective / self.get_effectif)*100
        return f'{result} %'

    def get_frequence_nbr(self, value):
        if not value in self.dict.keys():
            return f"The value {value} doesn't exist."
        effective = self.dict[value]
        result = int((effective / self.get_effectif)*100) if ((effective / self.get_effectif)*100) % 1 == 0 else (effective / self.get_effectif)*100
        return result


    #___________________GETTERS______________________

    @property
    def get_table(self):

        already = []
        is_val_1 = None

        top_row_1 = ''
        vals_row_1 = ''
        bot_row_1 = ''

        vals_row_2 = ''
        bot_row_2 = ''

        val_1 = self.name
        val_2 = 'Effectifs'

        if len(str(val_1)) > len(str(val_2)):
            size = len(str(val_1))
            text_1 = '| ' + str(val_1) + ' |'
            is_val_1 = True
        else:
            size = len(str(val_2))
            text_2 = '| ' + str(val_2) + ' |'

        line = '+' + '-' * size + 2 * '-' + '+'
        if is_val_1:
            text_2 = '| ' + (size - len(str(val_2))) // 2 * ' ' + ' ' + str(val_2) + (size - len(str(val_2))) // 2 * ' ' + ' ' + '|'
        else:
            text_1 = '| ' +(size - len(str(val_1))) // 2 * ' ' + ' ' + str(val_1) + (size - len(str(val_1))) // 2 * ' ' + ' ' + '|'

        top_row_1 += line
        bot_row_1 += line
        bot_row_2 += line
        vals_row_1 += text_1
        vals_row_2 += text_2

        val_1 = None
        val_2 = None

        for column in self.get_sorted :
            if column in already :
                continue
            text_1 = ''
            text_2 = ''
            is_val_1 = False
            val_1 = column
            already.append(val_1)
            val_2 = self.dict[column]
            if len(str(val_1)) > len(str(val_2)) :
                size = len(str(val_1))
                text_1 = ' ' + str(val_1) + ' |'
                is_val_1 = True
            else :
                size = len(str(val_2))
                text_2 = ' ' + str(val_2) + ' |'

            line = '-' * size + 2 * '-' + '+'
            if is_val_1 :
                text_2 = (size - len(str(val_2))) // 2 * ' ' + ' ' + str(val_2) + (size - len(str(val_2))) // 2 * ' ' + ' ' + '|'
            else :
                text_1 = (size - len(str(val_1))) // 2 * ' ' + ' ' + str(val_1) + (size - len(str(val_1))) // 2 * ' ' + ' ' + '|'

            top_row_1 += line
            bot_row_1 += line
            bot_row_2 += line
            vals_row_1 += text_1
            vals_row_2 += text_2



        row_1 = '\n' + top_row_1 + '\n' + vals_row_1 + '\n' + bot_row_1 + '\n'
        row_2 = vals_row_2 + '\n' + bot_row_2 + '\n'
        return (row_1 + row_2)
    @property
    def get_dict(self):
        return self.dict
    @property
    def get_array(self):
        result = []
        for key, value in self.dict.items():
            result += [key] * value

        return result

    @property
    def get_effectif(self):
        return self.effectif

    @property
    def get_sorted(self):
        for val in self.get_array :
            if not isinstance(val, (int, float)) :
                return "On ne peut pas faire cette opération ! Les valeurs données ne sont pas des nombres."
        return sorted(self.get_array)

    @property
    def get_moyenne(self):
        for val in self.get_array :
            if not isinstance(val, (int, float)) :
                return "On ne peut pas faire cette opération ! Les valeurs données ne sont pas des nombres."
        sum_of_all = sum(self.get_array)
        return sum_of_all / self.get_effectif


    @property
    def get_mediane(self):
        for val in self.get_sorted :
            if not isinstance(val, (int, float)) :
                return "On ne peut pas faire cette opération ! Les valeurs données ne sont pas des nombres."
        list = self.get_sorted
        if self.odd_or_even(self.get_effectif) :
            middle = (len(list) // 2) - 1
            return list[middle]
        else :
            middles = [(len(list) // 2) - 1, len(list) // 2]
            result = (list[middle[0]] + list[middle[1]]) / 2

    @property
    def get_étendue(self):
        for val in self.get_sorted :
            if not isinstance(val, (int, float)) :
                return "On ne peut pas faire cette opération ! Les valeurs données ne sont pas des nombres."
        min_val = self.get_sorted[0]
        max_val = self.last(self.get_sorted)
        return max_val - min_val

    @property
    def get_graph(self):
        already = []
        result = '  ↑\n'
        max_leg = 0
        arrow_h = ''
        leg_h = ''

        for column in self.get_sorted :
            if column in already :
                continue
            val = column
            already.append(val)
            print(already)
            print(val)
            val_eff = self.dict[val]
            print(val_eff)
            if val_eff > max_leg :
                max_leg = val_eff
            arrow_h += '---|'
            leg_h += f'   {val}'

        for legende, column in zip(range(max_leg, -1, -1), self.get_sorted) :
            already_1 = []
            added = f'{legende} |'
            for col in self.get_sorted :
                if col in already_1 :
                    continue
                val = col
                val_eff = self.dict[val]
                already_1.append(val)
                print(val)
                print(val_eff)
                added += '   '


                if val_eff >= legende :
                    added += 'o'
                else :
                    added += ' '
            if legende > 0 :
                result += added + '\n'
            else :
                result += added

        arrow_h += '--→\n'
        leg_h = '   ' + leg_h
        result += '\n  |' + arrow_h + leg_h
        return result

def ajouter(dicti) :
    added_key = None
    while not added_key :
        added_key = input('Quel est la valeur de la variable ajoutée ?' + '\n→')
        if not added_key :
            print('Merci de ne pas ajouter de variable vide.')
    if added_key in dicti.keys() :
        print('La variable existe déjà.')
        ajouter(dicti)

    passed = False
    while not passed :
        passed_ = False
        added_eff = input("Quel est l'effectif de la variable ajoutée ?"+ '\n→')
        is_float = False
        for car in added_eff :
            if car not in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'] :
                print('Ajouter seulement des nombres en effectif et séparer les virgules avec un point.')
                passed_ = True
                break
            if car == '.' :
                is_float = True
        if passed_ :
            continue
        else :
            passed = True
        if is_float :
            added_eff = float(added_eff)
        else :
            added_eff = int(added_eff)
    dicti[added_key] = added_eff
    return 'La variable a été ajoutée !'

def montrer(stat) :
    commande = input('\nQue voulez vous faire ? (écrire "aide" pour voir la liste)'+ '\n→')
    commande = commande.lower()
    commandes = ['aide', 'dictionnaire', 'liste' ,'effectif' ,'triée' ,'moyenne' ,'médiane' ,'étendue' ,'fréquence' ,'tableau' ,'graphique' ,'ajouter' ,'changer' ,'supprimer', 'arrêter']
    if commande not in commandes :
        return('Cette commande n\'est pas acceptée. (écrire "aide" pour voir la liste des commandes)')
    if commande == 'aide' :
        return "\n-Montrer les valeurs (dictionnaire) : dictionnaire\n-Montrer les valeurs (liste) : liste\n-Montrer l'effectif total : effectif\n-Montrer la liste triée : triée\n-Montrer la moyenne : moyenne\n-Montrer la médiane : médiane\n-Montrer l'étendue : étendue\n-Montrer la fréquence d'une variable (en pourcentages) : fréquence\n-Montrer un tableau : tableau\n-Montrer un graphique : graphique\n\n-Ajouter une variable : ajouter\n-Changer une variable : changer_var\n-Supprimer une variable : supprimer\n-Changer le nom : changer_nom\n-Arrêter le programme (!ATTENTION! : cette action est irréversible !) : arrêter"
    if commande == 'dictionnaire' :
        return stat.get_dict
    if commande == 'liste' :
        return stat.get_array
    if commande == 'effectif' :
        return stat.get_effectif
    if commande == 'triée' :
        return stat.get_sorted
    if commande == 'moyenne' :
        return stat.get_moyenne
    if commande == 'médiane':
        return stat.get_mediane
    if commande == 'étendue' :
        return stat.get_etendue
    if commande == 'fréquence' :
        var = input('De quelle variable voulez-vous voir la fréquence ?'+ '\n→')
        if stat.get_frequence != f"La variable {var} n'existe pas." :
            return 'La fréquence de var est de :', stat.get_frequence(var)
        else :
            return stat.get_frequence(var)
    if commande == 'tableau' :
        return stat.get_table
    if commande == 'graphique' :
        return stat.get_graph

    if commande == 'ajouter' :
        dict = stat.dict
        ajouter(dict)
        stat = Stats(dict, stat.name)
    if commande == 'changer' :
        var = input('Quelle variable voulez-vous modifier ?'+ '\n→')
        if var not in stat.get_dict.keys() :
            return "la variable n'existe pas."
        else :
            n_var_key = ''
            n_var_val = ''
            n_var_key_O = input('Modifier le nom ? O/N'+ '\n→')
            if n_var_key_O == 'O' :
                n_var_key = input('écrire le nouveau nom.'+ '\n→')
            else :
                n_var_key = var

            n_var_val_O = input('Modifier la valeur ? O/N'+ '\n→')
            if n_var_val_O == 'O' :
                n_var_val = input('écrire la nouvelle valeur.'+ '\n→')
            else :
                n_var_val = stat.dict[var]

            stat.dict.update(n_var_key, n_var_val)
            return 'La variable a été modifiée !'

    if commande == 'supprimer' :
        var = input('Quelle variable voulez-vous supprimer ?'+ '\n→')
        if var not in stat.get_dict.keys() :
            return "la variable n'existe pas."
        else :
            del stat.dict[val]
            return 'La variable a été supprimée !'

    if commande == 'arrêter' :
        broke = True
        print('Arrêt en cours ...')
        return

broke = False

def debut() :
    global broke
    name = input('Que sont les variables ajoutées ? (de quel type)'+ '\n→')
    values = {}
    print('Commençons par ajouter une variable.\n')
    ajouter(values)
    while not broke :
        print(montrer(Stats(values, name)))

print(Stats({
    1: 2,
    2: 5
}, 'd').get_graph)
debut()

By the way the text is in French because I’m French and I know there are tools to display some graphs but I don’t want to use them because I want it to be only with things I know and fully displayable on the terminal.

if you use a tool like https://pythontutor.com/ you can see your code being executed line by line and see where errors come from

please describe how to run your code and what steps to take to see the errors you are looking for help with

To precise it, it is some kind of data analyser that takes variables and size samples and return some graphs, tables, and information about.

To run the code you do :

  1. delete/hash these lines :

it is only for debugging, so it shows an example of graph (you can keep it if you want)

Launch th program now !

  1. Enter a type name for the variables (it is used when you print a table)
  2. The program starts by creating a new variable (because there is no stats when there is no data). First, you tell the name of this variable, then you tell the size sample. Example : I want to add 3 apples to the data. I will during the first required input write the name (here, apple) , then I will during the second input write the size sample (here, 3) Note that the size sample can only be a number.
  3. Then you arrive on the “main menu”. Here you can display different things : all values as a dict, an array, a sorted array, the average, the median, the range, the frequence of a value (that you will need to precise later) in %, a table and a graph. You can do all this by respectively write : “dictionnaire”, “liste”, “effectif”, “triée”, “moyenne”, “médiane”, “étendue”, “fréquence”, “tableau”, “graphique”. Besides, you can use “ajouter”, “changer”, “supprimer” to add, change or delete a variable. Finally, you can use “arrêter” to stop the program (or more precisely to make it so it comes to its end).

When you will have come to step 4, you will be able to see an error if you type “graphique” or “tableau”.

Is that answering your questions ?

EDIT : when I do all these steps, I get a KeyError : ‘O’

If you have an error please share the exact text of the error message.

Error messages are extremely useful and often contain the precise information that you need to resolve a problem. What do you think the error message tells you about your code?

Talk more about this value. What is it for? How do you use it?

I would avoid dict as a variable name, since that is a built-in Python class. Give your variables a more descriptive name, what is it? (not what data type it is)

Here is the full error message (XXX is the name of file, it’s private) :

Traceback (most recent call last):
File “XXX”, line 335, in
debut()
File “XXX”, line 329, in debut
print(montrer(Stats(values, name)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “XXX”, line 277, in montrer
return stat.get_table
^^^^^^^^^^^^^^
File “XXX”, line 86, in get_table
val_2 = self.dict[column]
~~~~~~~~~^^^^^^^^
KeyError: ‘O’

I’m on Pycharm btw, it might impact the error message ? I don’t really know.

So, self.dict is the regroupement of all values with their corresponding size sample, so self.dict[value] represents the size sample of the value we’re at in the loop for value in self.get_sorted(which iterates over a sorted-array representation of self.dict)

What does the error tell you is happening?

You have a dictionary, and a key.

What’s in the dictionary?
Where does the column variable come from?

I have a hard time believing that you wrote this entire program and you’re not able to dig in and investigate this error, frankly.

Okay I found it I feel so dumb :sob:

It’s in the sorted array when there is an error message.

If you found and fixed the problem there’s no reason to feel dumb.

Do you have a better idea of how to use the error message now?

How can you methodically investigate the problem next time?

Use print liberally to examine the values held in variables during execution. That’s the most basic debugging that you can do

1 Like

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.