计算pandas数据框架中列的稀有度

dzjeubhm  于 2023-08-01  发布在  其他
关注(0)|答案(2)|浏览(73)

我有一个pandas dataframe,它有一个country列和一个datetime列。在每一行滚动30天的时间里,我想计算一下这个国家被看到的几率。即,在过去30天的行中显示该国家的时间比例。
为了使这更复杂,我还想在每行的计算比例中包括更稀有的国家。即对于每一行,计算30天内与该行一样罕见或比该行更罕见的所有国家的总比例。因此,如果在过去30天内,连续10%的时间找到了一个国家,但在过去30天内,5%和2%的时间找到了另外两个国家,则稀有度得分为0.17。
例如。

country_data = {
    'country': ['USA', 'USA', 'USA', 'Canada', 'UK', 'Canada', 'USA', 'Canada', 'Canada', 'UK'],
    'datetime': [
        '2023-01-01', '2023-01-02', '2023-01-03', '2023-01-04', '2023-01-05',
        '2023-01-06', '2023-01-10', '2023-01-11', '2023-01-12', '2023-02-07'
    ],
}

字符串
country_df = pd.DataFrame(country_data)

country datetime
USA     2023-01-01
USA     2023-01-02
USA     2023-01-03
Canada  2023-01-04
UK      2023-01-05
Canada  2023-01-06
USA     2023-01-07
Canada  2023-01-08
Canada  2023-01-09
UK      2023-02-07


我想要的输出是:

country datetime    rarity_score
USA     2023-01-01  1.0 
USA     2023-01-02  1.0
USA     2023-01-03  1.0
Canada  2023-01-04  0.25
UK      2023-01-05  0.4
Canada  2023-01-06  0.5
USA     2023-01-10  1.0
Canada  2023-01-11  0.5
Canada  2023-01-12  1.0
UK      2023-02-07  0.5


每行稀有度分数的说明:
1.美国是一次和100%的时间在PST 30天
1.美国在过去30天里100%的时间被看到两次
1.美国在过去30天里被看了三次,而且是100%的时间
1.加拿大在过去30天内看到一次,有25%的时间
1.英国在过去30天内只出现一次,20%的时间出现。然而加拿大也被看到过一次,所以我们把它列入稀有度分数,使其40%
1.在过去的30天里,加拿大被看到了两次,40%的时间。然而英国是看到一次,所以我们包括在稀有度分数,使其50%
1.美国被看到4次,英国和加拿大都被看到比这更少,所以我们包括一切,使它100%
1.加拿大被看到3次,英国一次,比加拿大少,所以我们把它包括在内,使稀有度得分为50%
1.加拿大被认为是4倍,因为是美国和英国一次,所以我们包括everthing使分数100%
1.我们只看到30天内的前4行。英国被视为曾经是美国,所以这使得50%

o8x7eapl

o8x7eapl1#

假设日期是唯一的,您可以在30天内计算crosstab,然后计算rolling.sum,然后使用索引查找来检索每行的计数,并使用它来获取计数更少(或相等)的国家。最后,通过除以每天的总计数来计算分数:

country_df['datetime'] = pd.to_datetime(country_df['datetime'])

idx, cols = pd.factorize(country_df['country'])
tmp = pd.crosstab(country_df['datetime'], country_df['country']).rolling('30D').sum()

count = tmp.reindex(index=country_df['datetime'], columns=cols).to_numpy()[np.arange(len(tmp)), idx]
country_df['rarity_score'] = country_df['datetime'].map(tmp.where(tmp.le(count, axis=0)).sum(axis=1).div(tmp.sum(axis=1)))

字符串
输出量:

country   datetime  rarity_score
0     USA 2023-01-01          1.00
1     USA 2023-01-02          1.00
2     USA 2023-01-03          1.00
3  Canada 2023-01-04          0.25
4      UK 2023-01-05          0.40
5  Canada 2023-01-06          0.50
6     USA 2023-01-10          1.00
7  Canada 2023-01-11          0.50
8  Canada 2023-01-12          1.00
9      UK 2023-02-07          0.50


中间体:

# pd.crosstab(country_df['datetime'], country_df['country'])
country     Canada  UK  USA
datetime                   
2023-01-01       0   0    1
2023-01-02       0   0    1
2023-01-03       0   0    1
2023-01-04       1   0    0
2023-01-05       0   1    0
2023-01-06       1   0    0
2023-01-10       0   0    1
2023-01-11       1   0    0
2023-01-12       1   0    0
2023-02-07       0   1    0

# tmp
country     Canada   UK  USA
datetime                    
2023-01-01     0.0  0.0  1.0
2023-01-02     0.0  0.0  2.0
2023-01-03     0.0  0.0  3.0
2023-01-04     1.0  0.0  3.0
2023-01-05     1.0  1.0  3.0
2023-01-06     2.0  1.0  3.0
2023-01-10     2.0  1.0  4.0
2023-01-11     3.0  1.0  4.0
2023-01-12     4.0  1.0  4.0
2023-02-07     2.0  1.0  1.0

# tmp.where(tmp.le(count, axis=0))
country     Canada   UK  USA
datetime                    
2023-01-01     0.0  0.0  1.0
2023-01-02     0.0  0.0  2.0
2023-01-03     0.0  0.0  3.0
2023-01-04     1.0  0.0  NaN
2023-01-05     1.0  1.0  NaN
2023-01-06     2.0  1.0  NaN
2023-01-10     2.0  1.0  4.0
2023-01-11     3.0  1.0  NaN
2023-01-12     4.0  1.0  4.0
2023-02-07     NaN  1.0  1.0

6mw9ycah

6mw9ycah2#

我认为最好从原始的dataframe创建一个时间序列:

data = pd.DataFrame({'country': ['USA', 'USA', 'USA', 'Canada', 'UK', 'Canada', 'USA', 'Canada', 'Canada', 'UK']}, index = country_data["datetime"])

data.index = pd.to_datetime(data.index)
data = data["country"]

字符串
之后(这可能不是解决问题的最有效的方法-但假设你没有一个大的df就足够了):

from pandas.tseries.offsets import *

vals = {}
# assume index is already sorted
for i in data.index:
    last_30 = i - 30*Day() # allows slicing for the last 30 days
    d = data[last_30:i].value_counts() # get proportions of each country for the last 30 days
    vals[i] = (d.sort_values(ascending=True)/d.sum()).loc[:data[i]].sum()

pd.Series(vals)

相关问题