J'ai écrit une fonction qui demande à un utilisateur le nom de la colonne (ex. 'Age') ou le numéro de la colonne (0, 1, ... ou -1, -2, ...) et le renvoie s'il existe. J'aimerais savoir si ma solution peut être améliorée en termes de conception de code.

Pour clarifier, j'ai besoin de ce morceau de code pour une autre fonction qui calcule l'entropie de Shannon sur les dataframes pour lesquelles la colonne label doit être choisie manuellement.

import pandas as pd

df = pd.DataFrame({'A': [1,2,3], 'B':['a', 'b', 'c']})

def read(df):
    while True:
        column = input("Please, enter column name or number:") 
        if column.lstrip('-').isdecimal():
            if (-df.shape[1] > int(column)) or (int(column) >= df.shape[1]):
                print('Such column does not exist. Please, try again. \n')
                continue
            else:
                return df.iloc[:, int(column)]
        elif column not in df.columns:
            print('Such column does not exist. Please, try again. \n')
            continue
        else:
            return df.loc[:, column]
    return data[column]

read(df)
0
Александр Ермак 3 oct. 2021 à 00:40

2 réponses

Meilleure réponse

Les colonnes sont disponibles dans df.columns qui peuvent être utilisées pour obtenir les données souhaitées. Si la colonne n'est pas dans df.columns, essayez de la convertir en int pour indexer df.columns et utilisez un gestionnaire d'exceptions pour gérer les échecs.

import pandas as pd

df = pd.DataFrame({'A': [1,2,3], 'B':['a', 'b', 'c']})

def read(df):
    while True:
        column = input("Please, enter column name or number:")
        if column not in df.columns:
            try:
                column = df.columns[int(column)]
            except (IndexError, ValueError):
                print(f"Column {column!r} does not exist, Please try again.")
                continue
        break
    return df.loc[:, column]

print(read(df))
0
tdelaney 2 oct. 2021 à 21:55

L'approche EAFP dirait que nous devrions essayer de sélectionner à partir du DataFrame et gérer les erreurs qui surviennent, puisque pandas a déjà beaucoup travaillé pour voir si un indexeur est valide ou non :

Si nous allons complètement dans cette direction, nous nous retrouvons avec quelque chose comme :

def read(df_: pd.DataFrame) -> pd.Series:
    while True:
        column = input("Please, enter column name or number:")
        try:
            # Attempt to return the Column
            return df_[column]
        except KeyError:
            try:
                # Attempt to convert the column to int and return the column
                return df_.iloc[:, int(column)]
            except (ValueError, IndexError):
                # Print Message if both attempts fail
                print('Such column does not exist. Please, try again. \n')

J'ai changé le paramètre de fonction de df à df_ pour éviter d'occulter une variable à partir d'une portée externe.

Nous lisons d'abord dans la colonne, puis essayons de renvoyer le sous-ensemble DataFrame. Cela lève un KeyError s'il n'existe pas dans le DataFrame. Dans ce cas, nous essayons d'accéder aux valeurs de manière positionnelle. int(column) lèvera un ValueError s'il ne peut pas être converti en int, et iloc produira un IndexError si l'indexeur est hors limites.


Une version légèrement modifiée de ceci est:

def read(df_: pd.DataFrame) -> pd.Series:
    while True:
        try:
            column = input("Please, enter column name or number:")
            try:
                # Try to get int indexer from df_.columns
                indexer = df_.columns.get_loc(column)
            except KeyError:
                # Use int version of Column
                indexer = int(column)
            return df_.iloc[:, indexer]
        except (ValueError, IndexError):
            # Catch Invalid int conversion, or out of bounds indexes
            print('Such column does not exist. Please, try again. \n')

Ici, nous utilisons Index.get_loc qui " Obtenez l'emplacement entier, la tranche ou le masque booléen pour l'étiquette demandée." Cela génère également un KeyError si l'étiquette n'est pas présente dans les colonnes, cependant, dans ce cas, nous essayons de convertir column en indexer dans le except.

Cela signifie que indexer est garanti comme étant basé sur l'emplacement entier et peut être transmis à iloc. Ensuite, nous pouvons excepter le ValueError causé si la conversion int échoue, et le IndexError qui se produit si l'indexeur est hors limites.

0
Henry Ecker 2 oct. 2021 à 22:32