将值设置为DataFrame切片的副本

时间:2017-03-20 21:59:18

标签: python pandas numpy dataframe

我正在设置以下示例,该示例与我的情况和数据类似:

说,我有以下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()的情况下执行此操作?

3 个答案:

答案 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

对给定数据进行天真时间测试

enter image description here

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