【问题标题】:Pandas: select value from random column on each row熊猫:从每行的随机列中选择值
【发布时间】:2019-07-25 12:18:56
【问题描述】:

假设我有以下 Pandas DataFrame:

df = pd.DataFrame({
    'a': [1, 2, 3],
    'b': [4, 5, 6],
    'c': [7, 8, 9]
})
    a   b   c
0   1   4   7
1   2   5   8
2   3   6   9

我想生成一个新的pandas.Series,以便从 DataFrame 中的随机列中逐行选择该系列的值。因此,可能的输出将是系列:

0    7
1    2
2    9
dtype: int64

(在第 0 行中它随机选择了“c”,在第 1 行中它随机选择了“a”,在第 2 行中它再次随机选择了“c”)。

我知道这可以通过遍历行并使用random.choice 来选择每一行来完成,但是遍历行不仅性能差,而且可以说是“unpandonic”。此外, df.sample(axis=1) 会选择整个列,所以它们都将从同一列中选择,这不是我想要的。有没有更好的方法来使用矢量化 pandas 方法来做到这一点?

【问题讨论】:

    标签: python pandas dataframe random


    【解决方案1】:

    这是一个完全矢量化的解决方案。但请注意,它不使用 Pandas 方法,而是涉及对底层 numpy 数组的操作。

    import numpy as np
    
    indices = np.random.choice(np.arange(len(df.columns)), len(df), replace=True)
    

    示例输出为[1, 2, 1],对应于['b', 'c', 'b']

    然后使用它来切片 numpy 数组:

    df['random'] = df.to_numpy()[np.arange(len(df)), indices]
    

    结果:

       a  b  c  random
    0  1  4  7       7
    1  2  5  8       5
    2  3  6  9       9
    

    【讨论】:

    • 您能解释一下这部分语法的作用[np.arange(len(df)), indices] 吗?我在谷歌上搜索时遇到了问题。
    • 此外,您的答案在我的数据上给出的分布与@jfaccioni 的答案非常不同,这让我认为这个答案有问题(我的答案大大减少了)。不过它确实运行得更快。
    • @JosephGarvin - 该语法是对二维数组进行切片,使用两个相同长度的序列表示行索引和列索引。这里,np.arange(len(df)) 是行索引 - 在上面的示例中,它只是 [0, 1, 2]。随机选择的列索引位于indices 变量中(示例中为[1, 2, 1])。使用 [0, 1, 2][1, 2, 1] 进行切片会返回 (0, 1)、(1, 2) 和 (2, 1) 处的值。
    • @JosephGarvin - 我已经对我的解决方案循环了几百万次,并且在结果中没有看到任何异常,至少使用这个问题中的小示例数据集。如果您可以共享一个数据集(或让我知道如何生成一个数据集),该数据集显示我的解决方案与循环解决方案的结果不同,我将有兴趣看到。
    【解决方案2】:

    可能是这样的:

    pd.Series([np.random.choice(i,1)[0] for i in df.values])
    

    【讨论】:

    • 如果你需要制作很多这样的列,我发现这样效率更高:newdf = df.apply(lambda row : row.sample(n=10000, replace=True).values, axis=1, result_type="expand")
    【解决方案3】:

    这样就完成了(使用内置模块random):

    ddf = df.apply(lambda row : random.choice(row.tolist()), axis=1)
    

    或使用pandas sample:

    ddf = df.apply(lambda row : row.sample(), axis=1)
    

    两者具有相同的行为。 ddf 是你的系列。

    【讨论】:

    • 注意#1可以简化为df.apply(random.choice, axis=1)
    【解决方案4】:
    pd.DataFrame(
        df.values[range(df.shape[0]), 
                       np.random.randint(
                           0, df.shape[1], size=df.shape[0])])
    

    输出

        0
    0   4
    1   5
    2   9
    

    【讨论】:

      【解决方案5】:

      您可能仍然需要遍历每一行,同时在每一行中选择一个随机值 - 无论是使用 for 循环显式执行,还是使用您决定调用的任何函数隐式执行。

      但是,如果它适合您的风格,您可以使用列表推导将其简化为单行:

      result = pd.Series([random.choice(pd.iloc[i]) for i in range(len(df))])
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-04
        • 2017-08-21
        • 2018-06-25
        • 1970-01-01
        • 2014-06-26
        相关资源
        最近更新 更多