【问题标题】:Add Multiple Columns to Pandas Dataframe from Function从函数向 Pandas 数据框添加多列
【发布时间】:2019-02-26 09:45:10
【问题描述】:

我有一个熊猫数据框mydf,它有两列,两列都是日期时间数据类型:mydatemytime。我想再添加三列:hourweekdayweeknum

def getH(t): #gives the hour
    return t.hour
def getW(d): #gives the week number
    return d.isocalendar()[1] 
def getD(d): #gives the weekday
    return d.weekday() # 0 for Monday, 6 for Sunday

mydf["hour"] = mydf.apply(lambda row:getH(row["mytime"]), axis=1)
mydf["weekday"] = mydf.apply(lambda row:getD(row["mydate"]), axis=1)
mydf["weeknum"] = mydf.apply(lambda row:getW(row["mydate"]), axis=1)

sn-p 有效,但它的计算效率不高,因为它至少循环了数据帧 3 次。我只想知道是否有更快和/或更优化的方法来做到这一点。例如,使用zipmerge?例如,如果我只创建一个返回三个元素的函数,我应该如何实现呢?为了说明,函数将是:

def getHWd(d,t):
    return t.hour, d.isocalendar()[1], d.weekday()

【问题讨论】:

标签: python pandas


【解决方案1】:

以下是使用apply 的方法

说,df 就像

In [64]: df
Out[64]:
       mydate     mytime
0  2011-01-01 2011-11-14
1  2011-01-02 2011-11-15
2  2011-01-03 2011-11-16
3  2011-01-04 2011-11-17
4  2011-01-05 2011-11-18
5  2011-01-06 2011-11-19
6  2011-01-07 2011-11-20
7  2011-01-08 2011-11-21
8  2011-01-09 2011-11-22
9  2011-01-10 2011-11-23
10 2011-01-11 2011-11-24
11 2011-01-12 2011-11-25

为了便于阅读,我们将 lambda 函数取出来分隔行并定义它

In [65]: lambdafunc = lambda x: pd.Series([x['mytime'].hour,
                                           x['mydate'].isocalendar()[1],
                                           x['mydate'].weekday()])

然后,apply 并将结果存储到 df[['hour', 'weekday', 'weeknum']]

In [66]: df[['hour', 'weekday', 'weeknum']] = df.apply(lambdafunc, axis=1)

而且,输出是这样的

In [67]: df
Out[67]:
       mydate     mytime  hour  weekday  weeknum
0  2011-01-01 2011-11-14     0       52        5
1  2011-01-02 2011-11-15     0       52        6
2  2011-01-03 2011-11-16     0        1        0
3  2011-01-04 2011-11-17     0        1        1
4  2011-01-05 2011-11-18     0        1        2
5  2011-01-06 2011-11-19     0        1        3
6  2011-01-07 2011-11-20     0        1        4
7  2011-01-08 2011-11-21     0        1        5
8  2011-01-09 2011-11-22     0        1        6
9  2011-01-10 2011-11-23     0        2        0
10 2011-01-11 2011-11-24     0        2        1
11 2011-01-12 2011-11-25     0        2        2

【讨论】:

  • 谢谢,约翰。看起来不错。这种方法比原始帖子中的方法执行得更快。对于约 500 行的数据框,您的方法的平均性能为 0.1446926 秒,而原始方法的平均性能为 0.15949020 秒(10 次运行)。
  • lambdafunc = lambda x: -- 为什么不直接使用def lambdafunc(x): 呢?如果您要立即命名它,那么使用匿名函数没有多大意义。
【解决方案2】:

补充John Galt's answer

根据lambdafunc 执行的任务,您可能会通过将apply 的结果存储在新的DataFrame 中然后与原来的连接来体验一些加速:

lambdafunc = lambda x: pd.Series([x['mytime'].hour,
                                  x['mydate'].isocalendar()[1],
                                  x['mydate'].weekday()])

newcols = df.apply(lambdafunc, axis=1)
newcols.columns = ['hour', 'weekday', 'weeknum']
newdf = df.join(newcols) 

即使您没有看到速度提升,我也建议您使用join。您将能够避免直接在列上分配时可能弹出的(总是烦人的)SettingWithCopyWarning

SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

【讨论】:

    【解决方案3】:

    您可以通过让您应用的函数返回一个带有命名元素的pd.Series,以一种更简洁的方法来做到这一点:

    def process(row):
        return pd.Series(dict(b=row["a"] * 2, c=row["a"] + 2))
    
    
    my_df = pd.DataFrame(dict(a=range(10)))
    new_df = my_df.join(my_df.apply(process, axis="columns"))
    

    结果是:

       a   b   c
    0  0   0   2
    1  1   2   3
    2  2   4   4
    3  3   6   5
    4  4   8   6
    5  5  10   7
    6  6  12   8
    7  7  14   9
    8  8  16  10
    9  9  18  11
    

    【讨论】:

      【解决方案4】:
      def getWd(d):
          d.isocalendar()[1], d.weekday()
      def getH(t):
          return t.hour
      mydf["hour"] = zip(*df["mytime"].map(getH))
      mydf["weekday"], mydf["weeknum"] = zip(*df["mydate"].map(getWd))
      

      【讨论】:

      • 文卡特,嗨。 sn-p 返回一个 TypeError: zip argument #1 must support iteration
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-03-12
      • 2015-05-25
      • 2013-04-11
      • 2018-12-06
      • 2018-02-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多