【问题标题】:Sort tenors in finance notation在金融符号中排序男高音
【发布时间】:2017-02-06 03:41:14
【问题描述】:

我有一系列男高音

Tenors = np.array(['10Y', '15Y', '1M', '1Y', '20Y', '2Y', '30Y', '3M', '5Y', '6M', '9M'])

M 代表月份,Y 代表年份。正确排序的顺序(升序)将是

['1M', '3M', '6M', '9M', '1Y', '2Y', '5Y', '10Y', '15Y', '20Y', '30Y']

如何使用 python 和 scipy/numpy 实现这一点?由于tenors 源自pandas 数据框,因此基于pandas 的解决方案也可以。

【问题讨论】:

  • 制作男高音categorical data.
  • 你能有一个“18M”的男高音值吗?
  • @Jim 原则上是的,但是 afaik 它没有在市场上使用。
  • 顺便说一句,这是某种拖钓吗?
  • 不,不是拖钓。 “18M”是否存在对解决方案的结构有很大影响。如果我们知道任何超过 11 个月的期限都将以年表示,那么我们可以自动假设所有以年表示的期限都将排在任何以月表示的期限之后。但是,如果允许“18M”,那么您需要可以为您提供的代码,例如["1Y", "18M", "2Y"]。这是一个更复杂的问题。而且,您确定没有使用“18M”吗?我似乎记得有部分年份的票据(当我在银行工作时,18 个月是一个非常流行的术语)。

标签: python sorting pandas numpy finance


【解决方案1】:

方法 #1 这是一个基于 NumPy 的方法,使用 np.core.defchararray.replace -

repl = np.core.defchararray.replace
out = Tenors[repl(repl(Tenors,'M','00'),'Y','0000').astype(int).argsort()]

方法 #2 如果您正在使用像 '18M' 这样的字符串,我们需要做更多的工作,就像这样 -

def generic_case_vectorized(Tenors):
    # Get shorter names for functions
    repl = np.core.defchararray.replace
    isalph = np.core.defchararray.isalpha

    # Get scaling values
    TS1 = Tenors.view('S1')
    scale = repl(repl(TS1[isalph(TS1)],'Y','12'),'M','1').astype(int)

    # Get the numeric values
    vals = repl(repl(Tenors,'M',''),'Y','').astype(int)

    # Finally scale numeric values and use sorted indices for sorting input arr
    return Tenors[(scale*vals).argsort()]

方法 #3 这是另一种方法,虽然是一个循环的方法来再次处理一般情况 -

def generic_case_loopy(Tenors):
    arr = np.array([[i[:-1],i[-1]] for i in Tenors])
    return Tenors[(arr[:,0].astype(int)*((arr[:,1]=='Y')*11+1)).argsort()]

示例运行 -

In [84]: Tenors
Out[84]: 
array(['10Y', '15Y', '1M', '1Y', '20Y', '2Y', '30Y', '3M', '25M', '5Y',
       '6M', '18M'], 
      dtype='|S3')

In [85]: generic_case_vectorized(Tenors)
Out[85]: 
array(['1M', '3M', '6M', '1Y', '18M', '2Y', '25M', '5Y', '10Y', '15Y',
       '20Y', '30Y'], 
      dtype='|S3')

In [86]: generic_case_loopy(Tenors)
Out[86]: 
array(['1M', '3M', '6M', '1Y', '18M', '2Y', '25M', '5Y', '10Y', '15Y',
       '20Y', '30Y'], 
      dtype='|S3')

【讨论】:

  • 如果一个男高音是“18M”,会发生什么?
  • @JimMischel 添加了处理此类情况的方法。
【解决方案2】:

您可以使用str.extract 解析数字和值,然后通过astype 和最后sort_values 转换为intcategories

df = pd.DataFrame({'a':Tenors})
df[['b','c']] = df.a.str.extract("(\d+)([MY])", expand=True)
df.b = df.b.astype(int)
df.c = df.c.astype('category', ordered=True, categories=['M','Y'])
df = df.sort_values(['c','b'])
print (df)
      a   b  c
2    1M   1  M
7    3M   3  M
9    6M   6  M
10   9M   9  M
3    1Y   1  Y
5    2Y   2  Y
8    5Y   5  Y
0   10Y  10  Y
1   15Y  15  Y
4   20Y  20  Y
6   30Y  30  Y

print (df.a.tolist())
['1M', '3M', '6M', '9M', '1Y', '2Y', '5Y', '10Y', '15Y', '20Y', '30Y']

【讨论】:

  • 如果一个男高音是“18M”会怎样?
  • 然后它会像18M这样对这个值进行排序,但是OP没有这样的值。
【解决方案3】:
print sorted(Tenors, key=lambda Tenors: (Tenors[-1], int(Tenors[:-1])))

按最后一个字符排序,然后按整数值排序,直到最后一个字符

【讨论】:

  • 如果一个男高音是“18M”会怎样?
  • 没想到:可以更改它以便按总月数排序。因此,如果有 Y,则将该值乘以 12 if x[:-1]=='Y' then x[:-1]*12 else x[:-1]
【解决方案4】:

我选择了长解决方案,因为无论如何我都需要convert_tenors。这也解决了Jim's objection

import scipy

def convert_tenors(tenors):
    #convert tenors to years
    new_tenors = scipy.zeros_like(tenors,dtype=float)
    for i,o in enumerate(tenors):
        if(o[-1]=='M'):
            new_tenors[i] = int(o[:-1])/12
        else:
            new_tenors[i] = int(o[:-1])
    return new_tenors

def sort_tenors(tenors):
    #sort tenors in ascending order
    idx = scipy.argsort(convert_tenors(tenors))
    return tenors[idx]  

Tenors = scipy.array(['10Y', '15Y', '1M', '1Y', '20Y', '18M', '2Y', '30Y', '3M', '5Y', '6M', '9M'])
print(sort_tenors(Tenors))

返回

['1M' '3M' '6M' '9M' '1Y' '18M' '2Y' '5Y' '10Y' '15Y' '20Y' '30Y']

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-01-10
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 2021-11-26
    • 1970-01-01
    • 2011-04-15
    • 1970-01-01
    相关资源
    最近更新 更多