Ожидается выборка 1% или 3 в сценарии аудита - PullRequest
0 голосов
/ 10 апреля 2020

Я работаю над сценарием, который берет образец из каждой категории в файле Excel. Скрипт работает, но мои результаты не такие, как ожидалось - я получаю образец 2 вместе. Я хочу, чтобы скрипт брал 1, 3 или 5% от каждой категории, если только в этой категории нет ограниченного количества предметов; в этом случае я хочу получить образец 2. Я воспроизвел приведенный ниже код - извините за большой блок текста, я просто подумал, что было бы полезно увидеть весь код. Любая помощь, чтобы решить эту проблему, будет принята с благодарностью.

#imports
import pandas as pd

#read file
df = pd.read_excel(r"C:\Users\***\Desktop\***.xlsx")

#check for certain condition (Y)
df2 = df.loc[(df['Track Item']=='Y')]
print(len(df2))


#unique categories and subcategories
categories = df2['Category'].unique()
subcategories = df2['Subcategory'].unique()

#check for empty subcategories
subcategory = df2['Subcategory'].isnull().all()

#taking a sample based on whether subcategory is empty and the number of y-tracked items 
if subcategory == True:
    def sample_per(df2):
        if len(df2) >= 1500:
            for category in categories: 
                return df2.loc[(df2["Category"] == category)].apply(lambda x: x.sample(n=2) if 
                x.size*0.01 < 2 else x.sample(frac=0.01))
       elif len(df2) < 15000 and len(df2) > 10000:
            for category in categories: 
                return df2.loc[(df2["Category"] == category)].apply(lambda x: x.sample(n=2) if 
                x.size*0.03 < 2 else x.sample(frac=0.03))
       else:
            for category in categories: 
                return df2.loc[(df2["Category"] == category)].apply(lambda x: x.sample(n=2) if 
                x.size*0.05 < 2 else x.sample(frac=0.05))
else:
     def sample_per(df2):
        if len(df2) >= 1500:
            for subcategory in subcategories: 
                return df2.loc[(df2["Subcategory"] == subcategory)].apply(lambda x: x.sample(n=2) if 
                x.size*0.01 < 2 else x.sample(frac=0.01))
        elif len(df2) < 15000 and len(df2) > 10000:
            for subcategory in categories: 
                return df2.loc[(df2["Subcategory"] == subcategory)].apply(lambda x: x.sample(n=2) if 
                x.size*0.03 < 2 else x.sample(frac=0.03))
        else:
            for subcategory in subcategories: 
                return df2.loc[(df2["Subcategory"] == subcategory)].apply(lambda x: x.sample(n=2) if 
                x.size*0.05 < 2 else x.sample(frac=0.05))

    #result of sample_per function
    final = sample_per(df2)

Интервал не совпадает, потому что строки длинные - отступ правильный

1 Ответ

1 голос
/ 10 апреля 2020

Я вижу как минимум две проблемы в коде, который вы разместили. Во-первых, в функции return остановит оценку функции, как только она будет нажата. Это означает, что вы не собираетесь возвращать выборку для каждой категории, а только выбираете первую (под) категорию и затем полностью выходите из функции. Во-вторых, порядок ваших условий if означает, что среднее условие никогда не может быть запущено, и оба маленьких и больших кадра данных (<1500 или> = 15000 строк) будут обрабатываться этим третьим условием.

Вот функция, которая, я думаю, должна делать то, что вы хотите. Во-первых, я делаю тест категории / подкатегории, чтобы определить, какой столбец использовать (и, как следствие, устранить много повторяющегося кода) и получить (под) категории соответствующим образом. Во-вторых, я создаю пустой фрейм данных для хранения результатов. L oop добавит к этому различные подвыборки. Обратите внимание, что это не эффективный в вычислительном отношении способ сделать это, но это не должно быть проблемой, если ваши фреймы данных не становятся слишком большими. В-третьих, я создаю внутреннюю функцию для выполнения подвыборки. Наконец, я изменил порядок условий if / else. Начинаясь с самого большого и работая вниз, они взаимоисключающие и исчерпывающие все возможности. Обратите внимание, что последнее условие для, если у вас <1500 строк. В этом случае код ничего не вернет (или, точнее, функция вернет пустой фрейм данных). В вашем вопросе не указано, как вы хотите к этому относиться, поэтому пока он просто содержит <code>pass в качестве заполнителя.

def sample_per(df):
    # Conditionally set column name and categories variable
    if df['Subcategory'].isnull().all():
        col_name = 'Subcategory'
    else:
        col_name = 'Category'

    # Get unique (sub)categories
    categories = df[col_name].unique()

    # Create an empty dataframe to store results
    sample_df = pd.DataFrame()

    # Create an internal function to do the sampling
    def subsample(df, col_name, cat, frac):
        return df.loc[(df[col_name] == cat)].apply(lambda x: x.sample(n=2) if x.size*frac < 2 else x.sample(frac=frac))

    if df.shape[0] >= 15000:
        for cat in categories:
            sample_df = sample_df.append(subsample(df, col_name, cat, 0.05))
    elif df.shape[0] >= 10000:
        for cat in categories:
            sample_df = sample_df.append(subsample(df, col_name, cat, 0.03))
    elif df.shape[0] >= 1500:
        for cat in categories:
            sample_df = sample_df.append(subsample(df, col_name, cat, 0.01))
    else:
        pass

    # Return the sampled dataframe
    return sample_df

# result of sample_per function
final = sample_per(df2)

Конечно, вы также можете сделать все это, используя groupby:

def simple_sample(df):
    # Conditionally set column name
    if df['Subcategory'].isnull().all():
        col_name = 'Subcategory'
    else:
        col_name = 'Category'

    def subsample(df, col_name, frac):
        return df.groupby(col_name).apply(lambda x: x.sample(n=2) if x.size*frac < 2 else x.sample(frac=frac))

    if df.shape[0] >= 15000:
        return subsample(df, col_name, 0.05)
    elif df.shape[0] >= 10000:
        return subsample(df, col_name, 0.03)
    elif df.shape[0] >= 1500:
        return subsample(df, col_name, 0.01)
    else:
        return None
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...