代码之家  ›  专栏  ›  技术社区  ›  DrakeMurdoch

使用numpy random和pandas样本从DF中进行随机选择,而不是在100k跑后随机分布选择

  •  0
  • DrakeMurdoch  · 技术社区  · 2 年前

    我为我的D&D组,当施法时,选择是否另一个随机施法、顺序施法或什么都不发生(分别为50%、25%、25%)。

    为了做到这一点,我做了一个 pd.DataFrame 的数据 http://dnd5e.wikidot.com/spells ),然后附加两个数据帧(一个在拼写名称中写着“Original Cast”,另一个写着“Nothing Happens”),每个数据帧都带有 len == len(df)/2 以便有一个完整的 df 是原始大小的两倍,如下面的代码所示。

    import pandas as pd
    import numpy as np
    from os import urandom
    
    def create_df():
    
        df = pd.read_csv("all_spells.csv", encoding="utf-8")
        df = df.dropna(axis=0, subset="Spell Name")
        df_b = df[~df["Spell Name"].str.contains(r"\((.*?)\)")].reset_index(drop=True)
    
    
        OG_cast = ['Orginal Cast' for i in range(int(len(df.index)/2))]
        No_Magic = ['Nothing Happens' for i in range(int(len(df.index)/2))]
        Nadda = [None for i in range(int(len(df.index)/2))]
    
        df_same = pd.DataFrame(columns=df.columns,
                               data={
                                   df.columns[0]: OG_cast,
                                   df.columns[1]: Nadda,
                                   df.columns[2]: Nadda,
                                   df.columns[3]: Nadda,
                                   df.columns[4]: Nadda
                               })
        df_nothing = pd.DataFrame(columns=df.columns,
                               data={
                                   df.columns[0]: No_Magic,
                                   df.columns[1]: Nadda,
                                   df.columns[2]: Nadda,
                                   df.columns[3]: Nadda,
                                   df.columns[4]: Nadda
                               })
    
        df_full = pd.concat([df_b, df_same, df_nothing], axis=0).reset_index(drop=True)
    
        return df_full
    
    

    df_full.sample(n=10) 如下所示,以供参考。

    +-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+
    |     | Spell Name                 | School        | Casting Time   | Range   | Duration                       | Components   |
    +=====+============================+===============+================+=========+================================+==============+
    |  12 | Psychic Scream             | Enchantment   | 1 Action       | 90 feet | Instantaneous                  | S            |
    +-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+
    |  18 | True Polymorph             | Transmutation | 1 Action       | 30 feet | Concentration up to 1 hour     | V S M        |
    +-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+
    | 670 | Orginal Cast               |               |                |         |                                | nan          |
    +-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+
    | 193 | Conjure Woodland Beings    | Conjuration   | 1 Action       | 60 feet | Concentration up to 1 hour     | V S M        |
    +-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+
    | 795 | Orginal Cast               |               |                |         |                                | nan          |
    +-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+
    | 218 | Otilukes Resilient Sphere | Evocation     | 1 Action       | 30 feet | Concentration up to 1 minute   | V S M        |
    +-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+
    | 353 | Levitate                   | Transmutation | 1 Action       | 60 feet | Concentration up to 10 minutes | V S M        |
    +-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+
    | 839 | Nothing Happens            |               |                |         |                                | nan          |
    +-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+
    | 459 | Silent Image               | Illusion      | 1 Action       | 60 feet | Concentration up to 10 minutes | V S M        |
    +-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+
    | 719 | Orginal Cast               |               |                |         |                                | nan          |
    +-----+----------------------------+---------------+----------------+---------+--------------------------------+--------------+       
    

    然后我调用下面的脚本来了解施法时会发生什么。

        df = create_df()
    
        seed = int(np.random.uniform(0, len(df.index)*10))
        spell = df.sample(1, random_state=seed)['Spell Name'].values[0]
        print("The spell cast is:", spell)
    

    为了测试这是否给了我想要的分布(50%的时间是随机施法,25%什么都没发生,25%的时间是按预期施法),我运行

        OC = 0
        NH = 0
        N = 0
        for i in range(100000):
            seed = int(np.random.uniform(0, len(df.index)*10))
            arb = df.sample(1, random_state=seed)['Spell Name'].values[0]
            # print(arb)
            if arb == 'Orginal Cast':
                OC += 1
            elif arb == 'Nothing Happens':
                NH += 1
            else: N += 1
    
    
        print(OC, NH, N)
    

    我得到的不是50/25/25(对于上面提到的事情),而是非常一致的47/26.5/26.5。有人知道为什么会发生这种事吗?有人有更好的想法和随机抽样,以便更一致地获得正确的比率吗?

    1 回复  |  直到 2 年前
        1
  •  1
  •   mozway    2 年前

    你的方法似乎不必要地复杂。

    最终你想要的是“ 当施法时,随机选择是否施法,另一个随机施法,或者什么都不发生。 ".

    所以,只要这样做,就不需要创建复杂的熊猫结构:

    import numpy as np
    import pandas as pd
    
    # load dataset
    df = pd.read_html('http://dnd5e.wikidot.com/spells')[0]
    
    def cast_spell(df, spell, p=[0.25, 0.5, 0.25]):
        # check the input is valid
        assert spell in df['Spell Name'].values
        
        # randomly pick one of the outcomes
        match np.random.choice(['Original Spell', 'Other Spell', 'Nothing happens'], p=p):
            case 'Original Spell':
                return f'The "{spell}" spell was successfully cast.'
            case 'Nothing happens':
                return f'The "{spell}" spell failed: nothing happens.'
            case _:
                other = df.loc[df['Spell Name'].ne(spell), 'Spell Name'].sample(1).item()
                return f'The "{spell}" spell failed. "{other}" was cast instead.'
    
    cast_spell(df, 'True Strike')
    

    调用20次的示例 cast_spell(df, 'True Strike') :

    The "True Strike" spell failed. "Poison Spray" was cast instead.
    The "True Strike" spell was successfully cast.
    The "True Strike" spell failed. "Lightning Lure" was cast instead.
    The "True Strike" spell failed. "Thaumaturgy" was cast instead.
    The "True Strike" spell was successfully cast.
    The "True Strike" spell was successfully cast.
    The "True Strike" spell failed. "Light" was cast instead.
    The "True Strike" spell failed. "Infestation" was cast instead.
    The "True Strike" spell was successfully cast.
    The "True Strike" spell failed. "Mind Sliver" was cast instead.
    The "True Strike" spell failed: nothing happens.
    The "True Strike" spell failed. "Lightning Lure" was cast instead.
    The "True Strike" spell failed: nothing happens.
    The "True Strike" spell failed: nothing happens.
    The "True Strike" spell failed. "Mage Hand" was cast instead.
    The "True Strike" spell was successfully cast.
    The "True Strike" spell failed: nothing happens.
    The "True Strike" spell was successfully cast.
    The "True Strike" spell failed. "Primal Savagery" was cast instead.
    The "True Strike" spell failed. "Frostbite" was cast instead.
    

    纯python变体

    import pandas as pd
    import random
    
    df = pd.read_html('http://dnd5e.wikidot.com/spells')[0]
    
    spells = set(df['Spell Name'])
    
    def cast_spell(spells, spell, p=[0.25, 0.5, 0.25]):
        assert spell in spells
    
        choice = random.choices(['Original Spell', 'Other Spell', 'Nothing happens'],
                                weights=p)[0]
        if choice == 'Original Spell':
            return f'The "{spell}" spell was successfully cast.'
        elif choice == 'Nothing happens':
            return f'The "{spell}" spell failed: nothing happens.'
        else:
            other = random.choice(list(spells-{spell}))
            return f'The "{spell}" spell failed. "{other}" was cast instead.'
    
    cast_spell(spells, 'True Strike')