Python 选择数据帧的子集,每个变量具有N年的数据

Python 选择数据帧的子集,每个变量具有N年的数据,python,pandas,dataframe,Python,Pandas,Dataframe,我有一个数据集,显示了从1970年到2013年100多个国家的年度增长指标。并非所有国家都有所有年份的数据,年份最少的国家有30年的数据。我想把事情弄清楚,让所有国家都给我看30年的数据,去掉那些超过30年的国家的数据。我在下面提供一个例子 我曾想过使用循环从数据框中删除数据,直到所有国家出现30次,然后构建一个全新的数据框,但我相信有更好的解决方案 import pandas as pd data = {'Country':['Israel','Congo','Denmark',

我有一个数据集,显示了从1970年到2013年100多个国家的年度增长指标。并非所有国家都有所有年份的数据,年份最少的国家有30年的数据。我想把事情弄清楚,让所有国家都给我看30年的数据,去掉那些超过30年的国家的数据。我在下面提供一个例子

我曾想过使用循环从数据框中删除数据,直到所有国家出现30次,然后构建一个全新的数据框,但我相信有更好的解决方案

import pandas as pd

data = {'Country':['Israel','Congo','Denmark',
                   'Israel','Denmark',
                   'Israel','Congo',
                   'Israel','Congo','Denmark'],
        'Year':[2000,2000,2000,
                2001,2001,
                2002,2002,
                2003,2003,2003],
        'Value':[2.5,1.2,3.1,2.8,1.1,2.9,3.1,1.9,3.0,3.1]}
df = pd.DataFrame(data=data)
df
   Country  Year  Value
0   Israel  2000    2.5
1    Congo  2000    1.2
2  Denmark  2000    3.1
3   Israel  2001    2.8
4  Denmark  2001    1.1
5   Israel  2002    2.9
6    Congo  2002    3.1
7   Israel  2003    1.9
8    Congo  2003    3.0
9  Denmark  2003    3.1
上面的代码创建了一个数据框架,示例仅使用了3个国家和4年。从数据框中,你可以看到以色列有4年的数据,而丹麦和刚果只有3年。我想取消以色列的一年,这样所有国家都有三年。在真实的数据框架中,我想删除30年以上国家的年份,以便所有国家都有相同的年份,最好删除值最小的年份

下面是我使用for循环的解决方案,它使用了大量代码行:

gp = df.groupby('Country').groups #Group by country name
d = {} #Build dictionary Country Name => index list.

for i in gp: #Iterate over all countries until a list of 3 indeces is 
#reached for each country.
    d[i] = []
    for j in gp[i]:
        if len(d[i])<3: #A country appears once every year in the dataset,
#3 means 3 years. If a country appears more than 3 times, it will only 
#include the indices of the first 3 occurrences. 
            d[i].append(j)
indeces = [] #Gather the indeces to keep in the dataframe.
for i in d:
    for j in d[i]:
        if len(d[i])==3: #make sure the list has exactly 3 items
            indeces.append(j)

final_df = df.loc[indeces,['Country','Year','Value']]
final_df
#Now I have one less value for Israel, so all countries have 3 values.
   Country  Year  Value
1    Congo  2000    1.2
6    Congo  2002    3.1
8    Congo  2003    3.0
2  Denmark  2000    3.1
4  Denmark  2001    1.1
9  Denmark  2003    3.1
0   Israel  2000    2.5
3   Israel  2001    2.8
5   Israel  2002    2.9
gp=df.groupby('Country').groups#按国家名称分组
d={}#构建字典国家名称=>索引列表。
对于gp中的i:#迭代所有国家/地区,直到生成一个包含3个索引的列表
#每个国家都有。
d[i]=[]
对于gp[i]中的j:

如果len(d[i])您可以从“年份”列中的唯一值创建最近几年的列表,并使用布尔索引来使用该列表索引数据帧

recent_years = df.Year.unique()[-3:]
df[df.Year.isin(recent_years)]

    Country Year    Value
3   Israel  2001    2.8
4   Denmark 2001    1.1
5   Israel  2002    2.9
6   Congo   2002    3.1
7   Israel  2003    1.9
8   Congo   2003    3.0
9   Denmark 2003    3.1
如果您的年份值不一定按顺序排列,请使用numpy unique,它返回排序数组,而不是pandas unique()

这是另一个解决方案,每个国家最近3年都有回报。如果数据未按年份排序,则需要先对其进行排序

idx = df.groupby('Country').apply(lambda x: x['Year'].tail(3)).index
df.set_index(['Country', df.index]).reindex(idx).reset_index().drop('level_1', 1)

    Country Year    Value
0   Congo   2000    1.2
1   Congo   2002    3.1
2   Congo   2003    3.0
3   Denmark 2000    3.1
4   Denmark 2001    1.1
5   Denmark 2003    3.1
6   Israel  2001    2.8
7   Israel  2002    2.9
8   Israel  2003    1.9
如果数据未排序,请首先使用

df = df.sort_values(by = 'Year')

这是我使用熊猫的解决方案。它做了它必须做的事情,即使它使用了很多行代码。感谢@Vaishali的帮助:

threshold = 3 #Anything that occurs less than this will be removed, 
              #if it ocurrs more, the extra ocurrences with the least values 
              #will be removed.
newIndex = df.set_index('Country')#set new index to make selection by   
                                  #index posible.
values = newIndex.index.value_counts() #Count occurrences of index values.
to_keep = values[values>=threshold].index.values 
#Keep index values that ocurr >= threshold.
rank_df = newIndex.loc[to_keep,['Value','Year']]#Select rows and  
                                                #columns to keep.

#Sort values in descending order before meeting threshold.
rank_df = rank_df.sort_values('Value',ascending=False)
rank_df = rank_df.groupby(rank_df.index).head(threshold)#group again 
#Since values are sorted, head() will show highest values
rank_df = rank_df.groupby([rank_df.index,'Year']).mean() \
              .sort_values('Value',ascending=False)

#Finally, reset index to convert Year index into a column, and sort by year
rank_df.reset_index(level=1).sort_values('Year')
输出:

            Year    Value
Country         
Denmark     2000    3.1
Israel      2000    2.5
Congo       2000    1.2
Israel      2001    2.8
Denmark     2001    1.1
Congo       2002    3.1
Israel      2002    2.9
Denmark     2003    3.1
Congo       2003    3.0

谢谢Vaishali,但您的解决方案也删除了丹麦和刚果的一个值。我想要的是每个国家都有三个价值观。用for循环检查我的解决方案,看看我的意思。嗨@Vaishali!您的解决方案适用于某些情况,但不适用于某个国家出现次数少于3次的情况。假设一个国家只有一年的数据。该国应该从示例数据框中删除,因为将其平均值与拥有3年或以上数据的国家进行比较是不公平的。然而,你的解决方案非常好,它帮助我找到了答案,所以谢谢你!我正在使用pandas共享我的解决方案,尽管它使用了相当多的代码行。如果可以缩短一些,我会很感激你的意见。问题陈述中没有提到少于3个案例,所以解决方案无法解决。这将是一个小调整,以目前的解决方案,将张贴当我有机会工作
            Year    Value
Country         
Denmark     2000    3.1
Israel      2000    2.5
Congo       2000    1.2
Israel      2001    2.8
Denmark     2001    1.1
Congo       2002    3.1
Israel      2002    2.9
Denmark     2003    3.1
Congo       2003    3.0