Python 根据条件从数据帧中随机采样而不丢失数据

Python 根据条件从数据帧中随机采样而不丢失数据,python,pandas,dataframe,sample,Python,Pandas,Dataframe,Sample,我一直在VBA中做这件事,但非常感谢关于如何以pythonically方式做这件事的建议。 我有一个Excel表格,有8000多行和>25列。我需要将某些行标记为需要人工仔细检查。基本上,对于“姓名”列中的每个人,我们需要随机标记该人行中10%的行需要检查。我们不想丢失/删除/抑制其他行;它们需要保持可用性——这就是为什么我认为df.sample不起作用的原因。在实际数据中,将有20-30个唯一的名称,每个名称包含300-400行 到目前为止,我已经使用pandas将数据读入数据框,做了一些与这

我一直在VBA中做这件事,但非常感谢关于如何以pythonically方式做这件事的建议。 我有一个Excel表格,有8000多行和>25列。我需要将某些行标记为需要人工仔细检查。基本上,对于“姓名”列中的每个人,我们需要随机标记该人行中10%的行需要检查。我们不想丢失/删除/抑制其他行;它们需要保持可用性——这就是为什么我认为
df.sample
不起作用的原因。在实际数据中,将有20-30个唯一的名称,每个名称包含300-400行

到目前为止,我已经使用pandas将数据读入数据框,做了一些与这个问题无关的事情,并将数据框写回Excel文件。作为其中的一部分,我还在数据框架中创建了一个“Random_No.”列,认为这将是朝着我的目标迈出的有用的一步(“基于”我在VBA中的做法)…可能会为每个名称调用类似的内容

可能有一百万种方法可以做到这一点,我一直在摆弄各种各样的方法,但我真的很想听听你认为最有效的方法是什么。我似乎正在创建许多“助手”数据帧、系列和ArrayOfObjects……总体而言,这使得一切变得比可能需要的更复杂有没有一种简单的方法可以在数据框中实现这一点,而不是让新手Python一团糟

这里是数据的简化模式;“Needs_review”代表了我试图创建的专栏类型——同样,每个名字占10%。一如既往地感谢您的建议/指导

使用副作用

df = pd.DataFrame({
    'Name': [f"Name_{i}" for i in np.random.randint(0,10,10000)],
    'Col1': np.random.randn(10000)})

need_review = []
df.groupby(['Name']).agg(
    lambda x: need_review.extend(
        np.random.choice(
            x.index, int(0.1*len(x.index)), replace=False).tolist())).unstack()
df['Needs_Review'] = False
df.loc[need_review, 'Needs_Review'] = True

print (df.groupby(['Name', 'Needs_Review'])['Needs_Review'].count())
输出:

Name    Needs_Review
Name_0  False           871
        True             96
Name_1  False           925
        True            102
Name_2  False           895
        True             99
Name_3  False           890
        True             98
Name_4  False           842
        True             93
Name_5  False           932
        True            103
Name_6  False           911
        True            101
Name_7  False           932
        True            103
Name_8  False           909
        True            101
Name_9  False           898
        True             99
使用副作用

df = pd.DataFrame({
    'Name': [f"Name_{i}" for i in np.random.randint(0,10,10000)],
    'Col1': np.random.randn(10000)})

need_review = []
df.groupby(['Name']).agg(
    lambda x: need_review.extend(
        np.random.choice(
            x.index, int(0.1*len(x.index)), replace=False).tolist())).unstack()
df['Needs_Review'] = False
df.loc[need_review, 'Needs_Review'] = True

print (df.groupby(['Name', 'Needs_Review'])['Needs_Review'].count())
输出:

Name    Needs_Review
Name_0  False           871
        True             96
Name_1  False           925
        True            102
Name_2  False           895
        True             99
Name_3  False           890
        True             98
Name_4  False           842
        True             93
Name_5  False           932
        True            103
Name_6  False           911
        True            101
Name_7  False           932
        True            103
Name_8  False           909
        True            101
Name_9  False           898
        True             99

我在这里回答我自己的问题,但也呼吁任何改进。也可能这对其他Python新手有用

首先,再次感谢@mujjiga,他的解决方案在他生成的数据帧中运行得非常好,但我发现当我在自己的数据上使用它时,我得到了非常不稳定的结果。这是一个遗憾,因为我的数据花费了2到3秒,我不认为这太糟糕了。(话说回来,VBA花了那么长时间。)

所以我胡思乱想,想出了一些可行的办法,但我很感激任何改进的建议

首先,我尝试继续使用
apply(lambda
)执行某些操作。这完成了任务,但花费了惊人的23秒:

percent = 10 #<-- CHANGE THIS AS NEEDED
def do_it(x):
        gk  = df[['Name','Random']].groupby('Name')
        tm = gk.get_group(x)
        lst = sorted(tm.Random)
        lim = lst[int(len(lst)*percent*0.01)]
        return lim   #here and below, 'lim' represents the number below which we select a row for randomisation
df['Needs_Review']=df.apply(lambda x: 'Randomised' if x['Random'] < do_it(x['Name']) else ' ',axis=1)

好吧,那太酷了。那我为什么还要缠着你们大家呢?因为我仍然在使用“for循环”,对我的~25个名字中的每一个都进行迭代。我相信也有办法解决这个问题。使用这个数据集,性能的提高将是微不足道的,但这件事的原理一直困扰着我。有什么想法吗是否也使用名称循环?我对自己的术语笨拙表示歉意。

我在这里回答我自己的问题,但也呼吁进行任何改进。也许这对其他Python新手也有用

首先,再次感谢@mujjiga,他的解决方案在他生成的数据帧中运行得非常好,但我发现当我在自己的数据上使用它时,我得到了非常不稳定的结果。这是一个遗憾,因为我的数据需要2到3秒,我不认为这太糟糕。(这么说,在VBA中花了那么长时间。)

所以我胡思乱想,想出了一些可行的办法,但我很感激任何改进的建议

首先,我尝试继续使用
apply(lambda
)执行某些操作。这完成了任务,但花费了惊人的23秒:

percent = 10 #<-- CHANGE THIS AS NEEDED
def do_it(x):
        gk  = df[['Name','Random']].groupby('Name')
        tm = gk.get_group(x)
        lst = sorted(tm.Random)
        lim = lst[int(len(lst)*percent*0.01)]
        return lim   #here and below, 'lim' represents the number below which we select a row for randomisation
df['Needs_Review']=df.apply(lambda x: 'Randomised' if x['Random'] < do_it(x['Name']) else ' ',axis=1)

好吧,那太酷了。那我为什么还要缠着你们大家呢?因为我仍然在使用“for循环”,对我的~25个名字中的每一个都进行迭代。我相信也有办法解决这个问题。使用这个数据集,性能的提高将是微不足道的,但这件事的原理一直困扰着我。有什么想法吗也可以使用名称循环吗?很抱歉我在术语方面的笨拙。

这太棒了,谢谢@mujjiga。我只是想知道是否有其他Python新手会在选择之间感到困惑(作为列表)和np.random.choice。@PE同意,改为更好的变量名。这太棒了,谢谢@mujjiga。我只是想知道是否有其他Python新手会对choice(作为列表)和np.random.choice感到困惑。@PE同意,改为更好的变量名。