我正在设置以下示例,该示例与我的情况和数据类似:
说,我有以下DataFrame:
df = pd.DataFrame ({'ID' : [1,2,3,4],
'price' : [25,30,34,40],
'Category' : ['small', 'medium','medium','small']})
Category ID price
0 small 1 25
1 medium 2 30
2 medium 3 34
3 small 4 40
现在,我有以下功能,它根据以下逻辑返回折扣金额:
def mapper(price, category):
if category == 'small':
discount = 0.1 * price
else:
discount = 0.2 * price
return discount
现在我想要生成的DataFrame:
Category ID price Discount
0 small 1 25 0.25
1 medium 2 30 0.6
2 medium 3 40 0.8
3 small 4 40 0.4
所以我决定在列价上调用series.map,因为我不想使用apply。我正在处理大型DataFrame,并且映射比应用快得多。
我试过这样做:
for c in list(sample.Category.unique()):
sample[sample['Category'] == c]['Discount'] = sample[sample['Category'] == c]['price'].map(lambda x: mapper(x,c))
这并没有像我预期的那样工作,因为我试图在DataFrame的一个片段的副本上设置一个值。
我的问题是,
有没有办法在不使用df.apply()
的情况下执行此操作?
答案 0 :(得分:8)
使用np.where
-
mask = df.Category.values=='small'
df['Discount'] = np.where(mask,df.price*0.01, df.price*0.02)
另一种不同的方式 -
df['Discount'] = df.price*0.01
df['Discount'][df.Category.values!='small'] *= 2
为了提高性能,您可能希望使用数组数据,因此我们可以在使用df.price.values
的地方使用df.price
。
方法 -
def app1(df): # Proposed app#1 here
mask = df.Category.values=='small'
df_price = df.price.values
df['Discount'] = np.where(mask,df_price*0.01, df_price*0.02)
return df
def app2(df): # Proposed app#2 here
df['Discount'] = df.price.values*0.01
df['Discount'][df.Category.values!='small'] *= 2
return df
def app3(df): # @piRSquared's soln
df.assign(
Discount=((1 - (df.Category.values == 'small')) + 1) / 100 * df.price.values)
return df
def app4(df): # @MaxU's soln
df.assign(Discount=df.price * df.Category.map({'small':0.01}).fillna(0.02))
return df
计时 -
1)大数据集:
In [122]: df
Out[122]:
Category ID price Discount
0 small 1 25 0.25
1 medium 2 30 0.60
2 medium 3 34 0.68
3 small 4 40 0.40
In [123]: df1 = pd.concat([df]*1000,axis=0)
...: df2 = pd.concat([df]*1000,axis=0)
...: df3 = pd.concat([df]*1000,axis=0)
...: df4 = pd.concat([df]*1000,axis=0)
...:
In [124]: %timeit app1(df1)
...: %timeit app2(df2)
...: %timeit app3(df3)
...: %timeit app4(df4)
...:
1000 loops, best of 3: 209 µs per loop
10 loops, best of 3: 63.2 ms per loop
1000 loops, best of 3: 351 µs per loop
1000 loops, best of 3: 720 µs per loop
2)非常大的数据集:
In [125]: df1 = pd.concat([df]*10000,axis=0)
...: df2 = pd.concat([df]*10000,axis=0)
...: df3 = pd.concat([df]*10000,axis=0)
...: df4 = pd.concat([df]*10000,axis=0)
...:
In [126]: %timeit app1(df1)
...: %timeit app2(df2)
...: %timeit app3(df3)
...: %timeit app4(df4)
...:
1000 loops, best of 3: 758 µs per loop
1 loops, best of 3: 2.78 s per loop
1000 loops, best of 3: 1.37 ms per loop
100 loops, best of 3: 2.57 ms per loop
通过数据重用进一步提升 -
def app1_modified(df):
mask = df.Category.values=='small'
df_price = df.price.values*0.01
df['Discount'] = np.where(mask,df_price, df_price*2)
return df
计时 -
In [133]: df1 = pd.concat([df]*10000,axis=0)
...: df2 = pd.concat([df]*10000,axis=0)
...: df3 = pd.concat([df]*10000,axis=0)
...: df4 = pd.concat([df]*10000,axis=0)
...:
In [134]: %timeit app1(df1)
1000 loops, best of 3: 699 µs per loop
In [135]: %timeit app1_modified(df1)
1000 loops, best of 3: 655 µs per loop
答案 1 :(得分:4)
还使用了一些numpy
df.assign(
Discount=((1 - (df.Category.values == 'small')) + 1) / 100 * df.price.values)
Category ID price Discount
0 small 1 25 0.25
1 medium 2 30 0.60
2 medium 3 34 0.68
3 small 4 40 0.40
手术部分
(1 - (df.Category.values == 'small')) + 1) / 100 * df.price.values
这将生成一个布尔数组,并对其执行简单算术,以获得.01
和.02
。
对给定数据进行天真时间测试
Thx @Divakar指出这一点 对于那些使用python 2.x的人,你需要使用它来强制浮动问题。
df.assign(
Discount=((1 - (df.Category.values == 'small')) + 1) / 100. * df.price.values)
答案 2 :(得分:4)
这是另一个熊猫方法:
In [67]: df.assign(Discount=df.price * df.Category.map({'small':0.01}).fillna(0.02))
Out[67]:
Category ID price Discount
0 small 1 25 0.25
1 medium 2 30 0.60
2 medium 3 34 0.68
3 small 4 40 0.40