【问题标题】:Shuffle DataFrame rows随机播放 DataFrame 行
【发布时间】:2015-06-17 01:23:21
【问题描述】:

我有以下数据框:

    Col1  Col2  Col3  Type
0      1     2     3     1
1      4     5     6     1
...
20     7     8     9     2
21    10    11    12     2
...
45    13    14    15     3
46    16    17    18     3
...

DataFrame 是从 csv 文件中读取的。所有Type 1 的行都在最上面,然后是Type 2 的行,然后是Type 3 的行,等等。

我想打乱 DataFrame 行的顺序,以便混合所有 Type。可能的结果是:

    Col1  Col2  Col3  Type
0      7     8     9     2
1     13    14    15     3
...
20     1     2     3     1
21    10    11    12     2
...
45     4     5     6     1
46    16    17    18     3
...

我怎样才能做到这一点?

【问题讨论】:

    标签: python pandas dataframe permutation shuffle


    【解决方案1】:

    以下可能是其中一种方式:

    dataframe = dataframe.sample(frac=1, random_state=42).reset_index(drop=True)
    

    在哪里

    frac=1 表示数据帧的所有行

    random_state=42 表示在每次执行中保持相同的顺序

    reset_index(drop=True) 表示为随机数据帧重新初始化索引

    【讨论】:

      【解决方案2】:

      使用 Pandas 执行此操作的惯用方法是使用数据框的 .sample 方法对所有行进行采样而不进行替换:

      df.sample(frac=1)
      

      frac 关键字参数指定要在随机样本中返回的行的分数,因此frac=1 表示返回所有行(以随机顺序)。


      注意: 如果您希望就地改组您的数据框并重置索引,您可以这样做,例如

      df = df.sample(frac=1).reset_index(drop=True)
      

      在这里,指定drop=True 可防止.reset_index 创建包含旧索引条目的列。

      后续说明:虽然上面的操作可能看起来不像是就地,但python/pandas 足够聪明,不会再为shuffled 做一次malloc目的。也就是说,即使 reference 对象已更改(我的意思是 id(df_old)id(df_new) 不同),底层 C 对象仍然是相同的。为了证明确实如此,您可以运行一个简单的内存分析器:

      $ python3 -m memory_profiler .\test.py
      Filename: .\test.py
      
      Line #    Mem usage    Increment   Line Contents
      ================================================
           5     68.5 MiB     68.5 MiB   @profile
           6                             def shuffle():
           7    847.8 MiB    779.3 MiB       df = pd.DataFrame(np.random.randn(100, 1000000))
           8    847.9 MiB      0.1 MiB       df = df.sample(frac=1).reset_index(drop=True)
      
      

      【讨论】:

      • 是的,这正是我想在我的第一条评论中展示的内容,你必须分配两次必要的内存,这离做到这一点还差得很远。
      • @m-dz 如果我错了,请纠正我,但如果你不这样做.copy(),你仍然在引用同一个底层对象。
      • 好的,有时间我会用内存分析器运行它。谢谢
      • 不,它不会复制DataFrame,看看这一行:github.com/pandas-dev/pandas/blob/v0.23.0/pandas/core/…
      • @m-dz 我在上面运行了一个内存分析器。请参阅更新答案中的“后续说明”。
      【解决方案3】:

      还有什么有用的,如果您将它用于 Machine_learning 并希望始终分离相同的数据,您可以使用:

      df.sample(n=len(df), random_state=42)
      

      这可确保您始终保持随机选择的可复制性

      【讨论】:

      • 使用 frac=1 你不需要 n=len(df)
      【解决方案4】:

      TL;DRnp.random.shuffle(ndarray) 可以胜任。
      所以,在你的情况下

      np.random.shuffle(DataFrame.values)
      

      DataFrame 在底层使用 NumPy ndarray 作为数据持有者。 (您可以从DataFrame source code查看)

      因此,如果您使用np.random.shuffle(),它将沿多维数组的第一个轴对数组进行洗牌。但DataFrame 的索引保持不变。

      不过,有几点需要考虑。

      • 函数没有返回值。如果您想保留原始对象的副本,则必须在传递给函数之前这样做。
      • sklearn.utils.shuffle(),正如用户 tj89 建议的那样,可以指定 random_state 以及另一个控制输出的选项。您可能希望将其用于开发目的。
      • sklearn.utils.shuffle() 更快。但将随机播放 DataFrame 的轴信息(索引、列)以及它包含的 ndarray

      基准测试结果

      sklearn.utils.shuffle()np.random.shuffle() 之间。

      nd​​array

      nd = sklearn.utils.shuffle(nd)
      

      0.10793248389381915 秒。 快 8 倍

      np.random.shuffle(nd)
      

      0.8897626010002568 秒

      数据帧

      df = sklearn.utils.shuffle(df)
      

      0.3183923360193148 秒。 快 3 倍

      np.random.shuffle(df.values)
      

      0.9357550159329548 秒

      结论:如果可以将轴信息(索引,列)与ndarray一起洗牌,请使用sklearn.utils.shuffle()。否则,使用np.random.shuffle()

      使用的代码

      import timeit
      setup = '''
      import numpy as np
      import pandas as pd
      import sklearn
      nd = np.random.random((1000, 100))
      df = pd.DataFrame(nd)
      '''
      
      timeit.timeit('nd = sklearn.utils.shuffle(nd)', setup=setup, number=1000)
      timeit.timeit('np.random.shuffle(nd)', setup=setup, number=1000)
      timeit.timeit('df = sklearn.utils.shuffle(df)', setup=setup, number=1000)
      timeit.timeit('np.random.shuffle(df.values)', setup=setup, number=1000)
      

      【讨论】:

      • df = df.sample(frac=1) 不做与df = sklearn.utils.shuffle(df) 完全相同的事情吗?根据我的测量,df = df.sample(frac=1) 更快,并且似乎执行完全相同的操作。他们也都分配了新的内存。 np.random.shuffle(df.values) 最慢,但不分配新内存。
      • 在将轴与数据一起洗牌方面,它似乎可以做同样的事情。是的,使用上面相同的代码,df.sample(frac=1) 似乎比 sklearn.utils.shuffle(df) 快 20%。或者您可以通过sklearn.utils.shuffle(ndarray) 获得不同的结果。
      • ...而且对索引进行洗牌确实不好,因为它可能导致某些函数难以追踪问题,这些问题要么重置索引,要么依赖于基于最大索引的假设行数。例如,h2o_model.predict() 发生了这种情况,它会重置返回的预测帧的索引。
      【解决方案5】:

      这是另一种方式:

      df['rnd'] = np.random.rand(len(df)) df = df.sort_values(by='rnd', inplace=True).drop('rnd', axis=1)

      【讨论】:

        【解决方案6】:

        (我没有足够的声誉在顶帖发表评论,所以我希望其他人可以为我这样做。)有人担心第一种方法: p>

        df.sample(frac=1)
        

        制作了深拷贝或只是更改了数据框。我运行了以下代码:

        print(hex(id(df)))
        print(hex(id(df.sample(frac=1))))
        print(hex(id(df.sample(frac=1).reset_index(drop=True))))
        

        我的结果是:

        0x1f8a784d400
        0x1f8b9d65e10
        0x1f8b9d65b70
        

        这意味着该方法返回相同的对象,如上一条评论中所建议的那样。所以这个方法确实做了一个打乱的copy

        【讨论】:

        • 请查看原始答案的后续说明。在那里您会看到,即使引用已更改(不同的ids),底层对象也没有被复制。换句话说,该操作实际上是在内存中进行的(尽管这并不明显)。
        • 我希望底层的 ndarray 是相同的,但迭代器是不同的(并且是随机的),因此尽管元素顺序发生了变化,但内存消耗的变化很小。
        【解决方案7】:

        AFAIK 最简单的解决方案是:

        df_shuffled = df.reindex(np.random.permutation(df.index))
        

        【讨论】:

        • 请注意,这会更改原始 df 中的索引,并生成一个副本,您将其保存到 df_shuffled 中。但是,更令人担忧的是,任何不依赖于索引的东西,例如 `df_shuffled.iterrows()' 都会产生与 df 完全相同的顺序。总之,慎用!
        • @Jblasco 这是不正确的,原来的 df 完全没有改变。 np.random.permutation 的文档:“...如果 x 是一个数组,则制作一个 copy 并随机打乱元素”。 DataFrame.reindex 的文档:“除非新索引等同于当前索引并且 copy=False,否则会生成一个 新对象”。所以答案是完全安全的(尽管会生成一个副本)。
        • @AndreasSchörgenhumer,感谢您指出这一点,您部分正确!我知道我已经尝试过了,所以我做了一些测试。不管np.random.permutation says 的文档是什么,并且取决于numpy 的版本,你会得到我描述的或你提到的效果。使用 numpy > 1.15.0,创建一个数据框并执行一个普通的np.random.permutation(df.index),原始 df 中的索引会发生变化。 numpy == 1.14.6 的情况并非如此。因此,我比以往任何时候都更重复我的警告:由于无法预料的副作用和版本依赖性,这种做事方式很危险。
        • @Jblasco 你是对的,谢谢你的详细信息。我正在运行 numpy 1.14,所以一切正常。对于 numpy 1.15,某处似乎有一个 bug。鉴于此错误,您的警告目前确实是正确的。但是,由于它是一个 错误 并且文档说明了其他行为,我仍然坚持我之前的声明,即答案是安全的(鉴于文档确实反映了实际行为,我们通常应该能够依靠)。
        • @AndreasSchörgenhumer,说实话,不太确定这是一个错误还是一个功能。文档保证数组的副本,而不是Index 类型...无论如何,我的建议/警告基于实际行为,而不是文档:p
        【解决方案8】:

        通过在这种情况下获取样本数组 index 并随机化其顺序,然后将数组设置为数据帧的索引来打乱 pandas 数据帧。现在根据索引对数据框进行排序。这是你的洗牌数据框

        import random
        df = pd.DataFrame({"a":[1,2,3,4],"b":[5,6,7,8]})
        index = [i for i in range(df.shape[0])]
        random.shuffle(index)
        df.set_index([index]).sort_index()
        

        输出

            a   b
        0   2   6
        1   1   5
        2   3   7
        3   4   8
        

        在上面的代码中将你的数据框插入到我的位置。

        【讨论】:

        • 我更喜欢这种方法,因为这意味着如果我需要通过将随机索引存储到变量来精确地重现我的算法输出,则可以重复随机播放。
        【解决方案9】:

        你可以简单地使用 sklearn 来做这个

        from sklearn.utils import shuffle
        df = shuffle(df)
        

        【讨论】:

        • 这很好,但您可能需要在改组后重置索引:df.reset_index(inplace=True, drop=True)
        【解决方案10】:

        您可以通过使用打乱索引进行索引来打乱数据帧的行。为此,您可以例如使用np.random.permutation(但np.random.choice 也是一种可能):

        In [12]: df = pd.read_csv(StringIO(s), sep="\s+")
        
        In [13]: df
        Out[13]: 
            Col1  Col2  Col3  Type
        0      1     2     3     1
        1      4     5     6     1
        20     7     8     9     2
        21    10    11    12     2
        45    13    14    15     3
        46    16    17    18     3
        
        In [14]: df.iloc[np.random.permutation(len(df))]
        Out[14]: 
            Col1  Col2  Col3  Type
        46    16    17    18     3
        45    13    14    15     3
        20     7     8     9     2
        0      1     2     3     1
        1      4     5     6     1
        21    10    11    12     2
        

        如果您想保持索引编号为 1、2、..、n,如您的示例所示,您可以简单地重置索引:df_shuffled.reset_index(drop=True)

        【讨论】:

          猜你喜欢
          • 2023-03-28
          • 1970-01-01
          • 1970-01-01
          • 2023-01-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多