【问题标题】:Efficient way to assign values from another column pandas df从另一列 pandas df 分配值的有效方法
【发布时间】:2019-02-12 09:01:51
【问题描述】:

我正在尝试创建一个更高效的脚本,该脚本根据另一列中的值创建一个新的column。下面的脚本执行此操作,但我一次只能选择一个string。我想对所有单个值执行此操作。

对于下面的df,我目前正在对Location 中的每个string 运行脚本。但是,我想在所有uniquestrings 上运行脚本。

关于如何分配新列的说明:Location 中的每个 string 获取 Day 中前 3 个唯一项的值。因此,对于Location 中的每个值,都会为Day 中的前三个唯一值分配一个新字符串。

import pandas as pd
import numpy as np

d = ({
    'Day' : ['Mon','Tues','Wed','Wed','Thurs','Thurs','Fri','Mon','Sat','Fri','Sun'],                 
    'Location' : ['Home','Home','Away','Home','Away','Home','Home','Home','Home','Away','Home'],        
    })

df = pd.DataFrame(data=d)

#Select value
mask = df['Location'] == 'Home'
df1 = df[mask].drop_duplicates('Day')
d = dict(zip(df1['Day'], np.arange(len(df1)) // 3 + 1))

df.loc[mask, 'Assign'] = df.loc[mask, 'Day'].map(d)

目前我正在选择['Location'] 中的每个值,例如mask = df['Location'] == 'Home'

我想在所有价值观上都这样做。例如mask = df['Location'] == All unique values

预期输出:

      Day Location Assign
0     Mon     Home     C1
1    Tues     Home     C1
2     Wed     Away     C2
3     Wed     Home     C1
4   Thurs     Away     C2
5   Thurs     Home     C3
6     Fri     Home     C3
7     Mon     Home     C1
8     Sat     Home     C3
9     Fri     Away     C2
10    Sun     Home     C4

【问题讨论】:

    标签: python pandas loops numpy if-statement


    【解决方案1】:

    你可以使用:

    def f(x):
        #get unique days
        u = x['Day'].unique()
        #mapping dictionary
        d = dict(zip(u, np.arange(len(u)) // 3 + 1))
        x['new'] = x['Day'].map(d)
        return x
    
    df = df.groupby('Location', sort=False).apply(f)
    #add Location column
    s = df['new'].astype(str) + df['Location']
    #encoding by factorize
    df['new'] = pd.Series(pd.factorize(s)[0] + 1).map(str).radd('C')
    print (df)
          Day Location new
    0     Mon     Home  C1
    1    Tues     Home  C1
    2     Wed     Away  C2
    3     Wed     Home  C1
    4   Thurs     Away  C2
    5   Thurs     Home  C3
    6     Fri     Home  C3
    7     Mon     Home  C1
    8     Sat     Home  C3
    9     Fri     Away  C2
    10    Sun     Home  C4
    

    【讨论】:

      【解决方案2】:
      # DataFrame Given
      df = pd.DataFrame({
          'Day' : ['Mon','Tues','Mon','Wed','Thurs','Fri','Mon','Sat','Sun','Tues'],                 
          'Location' : ['Home','Home','Away','Home','Home','Home','Home','Home','Home','Away'],                   
           })
      Unique_group = ['Mon','Tues','Wed']
      df['Group'] = df['Day'].apply(lambda x:1 if x in Unique_group else 2)
      df['Assign'] = np.zeros(len(df))
      # Assigning the ditionary values for output from numeric
      vals = dict([(i,'C'+str(i)) for i in range(len(df))])
      

      循环剪切每一行的数据框并检查前一个“分配”列信息以分配新值

      for i in range(1,len(df)+1,1):
          # Slicing the Dataframe line by line
          df1 = df[:i]
          # Incorporating the conditions of Group and Location
          df1 = df1[(df1.Location == df1.Location.loc[i-1]) & (df1.Group == df1.Group.loc[i-1]) ]
          # Writing the 'Assign' value for the first line of sliced df
          if len(df1)==1:
              df.loc[i-1,'Assign'] = df[:i].Assign.max()+1
          # Writing the 'Assign value based on previous values if it has contiuos 2 values of same group
          elif (df1.Assign.value_counts()[df1.Assign.max()] <3):
              df.loc[i-1,'Assign'] = df1.Assign.max()
          # Writing 'Assign' value for new group
          else:
              df.loc[i-1,'Assign'] = df[:i]['Assign'].max()+1
      df.Assign = df.Assign.map(vals)
      

      输出:

           Day    Location    Group   Assign
      0   Mon Home    1   C1
      1   Tues    Home    1   C1
      2   Mon Away    1   C2
      3   Wed Home    1   C1
      4   Thurs   Home    2   C3
      5   Fri Home    2   C3
      6   Mon Home    1   C4
      7   Sat Home    2   C3
      8   Sun Home    2   C5
      9   Tues    Away    1   C2
      

      【讨论】:

      • 感谢@Naga Kiran,但我希望按照预期的输出将其作为df 订购。
      • sry ,我以为您只是在对 df 进行分组时遇到问题,我编辑了代码以满足您的要求,但即使在连续 3 次之后,我也无法弄清楚 C1 在“第 6 行”中是如何重复的给定组的值
      • 感谢@Naga Kiran。是否有可能拥有“任何 3 个唯一值”。而不是专门['Mon','Tues','Wed']。不过,这会给我一些东西。
      • 是的@Maxibon 我们可以有任何唯一值。我刚刚编辑了代码,在 Unique values list 中提到了您所需的唯一值,然后只有那些列为 group1 并在 group2 中休息的值
      • 对不起。任何 3 个非硬编码的唯一字符串。 3 个唯一值的组合是无限的。例如,它可能是Mon, Tues, WedSun, Thurs, Tues。基本上任何 3 个字符串。
      【解决方案3】:

      在第二次尝试时有效。

      这个问题很难理解。

      我确信这应该用 pandas 来完成 groupby() 和数据框合并,如果你检查 这个回复的历史你可以看到我是如何 更改了答案以替换更慢的 Python 带有快速 Pandas 代码的代码。

      下面的代码首先计算每个唯一值 位置,然后使用辅助数据框 创造最终价值。

      我建议将此代码粘贴到 Jupyter 笔记本中 并检查中间步骤。

      import pandas as pd
      import numpy as np
      
      d = ({
          'Day' : ['Mon','Tues','Wed','Wed','Thurs','Thurs','Fri','Mon','Sat','Fri','Sun'],                 
          'Location' : ['Home','Home','Away','Home','Away','Home','Home','Home','Home','Away','Home'],        
          })
      
      df = pd.DataFrame(data=d)
      
      # including the example result
      df["example"] = pd.Series(["C" + str(e) for e in [1, 1, 2, 1, 2, 3, 3, 1, 3, 2, 4]])
      
      # this groups days per location
      s_grouped = df.groupby(["Location"])["Day"].unique()
      
      # This is the 3 unique indicator per location
      df["Pre-Assign"] = df.apply(
          lambda x: 1 + list(s_grouped[x["Location"]]).index(x["Day"]) // 3, axis=1
      )
      
      # Now we want these unique per combination
      df_pre = df[["Location", "Pre-Assign"]].drop_duplicates().reset_index().drop("index", 1)
      df_pre["Assign"] = 'C' + (df_pre.index + 1).astype(str)
      
      # result
      df.merge(df_pre, on=["Location", "Pre-Assign"], how="left")
      

      结果

      其他数据帧/系列:

      【讨论】:

      • 这是问题的症结所在。我可以在任何特定的string 上运行它,例如HomeAway。但是,如果我有很多strings,那么一次在一个string 上运行脚本就会变得低效。我希望得到在所有uniquestrings上运行脚本的东西@
      • 你试过我的例子吗?它使用 panda 的内部分组功能,非常擅长解决这个问题——而且速度很快。比 Python 循环快 100 倍。请注意,我的代码从未提及“Home”或“Away”。 “离开”没有同时分组的唯一原因是您的示例在那里是空的。顺便说一句,确保将.astype("category", inplace=True) 应用于任何数据框列,这些列大多具有相同的值。它将减少内存使用,并使您处理大型数据帧的速度更快。
      • @576i 最好在等待时删除答案。在这种情况下,有些人可能会选择投反对票,我不想看到有人试图提供帮助。
      • 好评论...我终于明白了这一点,但稍后会发布回复,因为让组正确很难...但它会起作用。
      • 顺便说一句,我进行了 %%timeit 性能检查,这个解决方案花费的时间不到其他解决方案的一半(即使数据帧如此小,数据帧大且适当,差异应该更大)
      【解决方案4】:

      没有那么漂亮,但比 groupby/apply 方法快得多......

      def get_ordered_unique(a):
          u, idx = np.unique(a, return_index=True)
          # get ordered unique values
          return a[np.sort(idx)]
      
      # split ordered unique value array into arrays of size 3
      def find_ugrps(a):
          ord_u = get_ordered_unique(a)
      
          if ord_u.size > 3:
              split_idxs = [i for i in range(1, ord_u.size) if i % 3 == 0]
              u_grps = np.split(ord_u, split_idxs)
          else:
              u_grps = [ord_u]
      
          return u_grps
      
      locs = pd.factorize(df.Location)[0] + 1
      days = pd.factorize(df.Day)[0] + 1
      
      assign = np.zeros(days.size).astype(int)
      unique_locs = get_ordered_unique(locs)
      
      i = 0
      for loc in unique_locs:
          i += 1
          loc_idxs = np.where(locs == loc)[0]
          # find the ordered unique day values for each loc val slice
          these_unique_days = get_ordered_unique(days[loc_idxs])
          # split into ordered groups of three
          these_3day_grps = find_ugrps(these_unique_days)
          # assign integer for days found within each group
          for ugrp in these_3day_grps:
              day_idxs = np.where(np.isin(days[loc_idxs], ugrp))[0]
              np.put(assign, loc_idxs[day_idxs], i)
              i += 1
      
      # set proper ordering within assign array using factorize
      df['Assign'] = (pd.factorize(assign)[0] + 1)
      df['Assign'] = 'C' + df['Assign'].astype(str)
      
      print(df)
      
            Day Location Assign
      0     Mon     Home     C1
      1    Tues     Home     C1
      2     Wed     Away     C2
      3     Wed     Home     C1
      4   Thurs     Away     C2
      5   Thurs     Home     C3
      6     Fri     Home     C3
      7     Mon     Home     C1
      8     Sat     Home     C3
      9     Fri     Away     C2
      10    Sun     Home     C4
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-12
        • 2020-12-13
        • 2020-12-06
        • 1970-01-01
        • 1970-01-01
        • 2018-11-11
        相关资源
        最近更新 更多