Pandas DataFrame из данных WB WDI: объединить столбцы года в переменную года и объединить строки

У меня есть набор данных (файл .tsv) со следующими столбцами. (Это новый всеобъемлющий загружаемый набор данных от Всемирного банка. . Хороший!)

country countrycode varname 1960 1961 1962
afghanistan AFG GDP 5.6 5.7 5.8
afghanistan AFG Gini .77 .78 .75
afghanistan AFG educ 8.1 8.2 8.3
afghanistan AFG pop 888 889 890
albania ALB GDP 6.6 6.7 6.8
albania ALB Gini .45 .46 .47
albania ALB educ 6.2 6.3 6.4
albania ALB pop 777 778 779

Мне нужен DataFrame pandas с ['GDP','Gini','edu','pop'] в качестве столбцов, а также ['country', 'countrycode', 'year']. Таким образом, значения для «года» в настоящее время являются столбцами! И я бы хотел, чтобы для каждой комбинации страна-год была только одна строка.

Например, столбцы и первая строка будут

country countrycode year GDP Gini educ pop
afghanistan AFG 1960 5.6 .77 8.1 888

Это похоже на какой-то сложный поворот или противоположность «расплаву», но я не могу этого понять.


person CPBL    schedule 03.05.2013    source источник


Ответы (3)


In [59]: df
Out[59]:
       country countrycode varname    1960    1961    1962
0  afghanistan         AFG     GDP    5.60    5.70    5.80
1  afghanistan         AFG    Gini    0.77    0.78    0.75
2  afghanistan         AFG    educ    8.10    8.20    8.30
3  afghanistan         AFG     pop  888.00  889.00  890.00
4      albania         ALB     GDP    6.60    6.70    6.80
5      albania         ALB    Gini    0.45    0.46    0.47
6      albania         ALB    educ    6.20    6.30    6.40
7      albania         ALB     pop  777.00  778.00  779.00

In [60]: df = df.set_index(['country', 'countrycode', 'varname'])

In [61]: df.columns.name = 'year'

In [62]: df.stack().unstack('varname')
Out[62]:
varname                       GDP  Gini  educ  pop
country     countrycode year
afghanistan AFG         1960  5.6  0.77   8.1  888
                        1961  5.7  0.78   8.2  889
                        1962  5.8  0.75   8.3  890
albania     ALB         1960  6.6  0.45   6.2  777
                        1961  6.7  0.46   6.3  778
                        1962  6.8  0.47   6.4  779

Последний представляет собой кадр с MutliIndex, вы можете выполнить reset_index, чтобы переместить MultiIndex в обычные столбцы.

person Wouter Overmeire    schedule 03.05.2013
comment
Магия. Спасибо. Что нужно сделать, чтобы это стало легко понять? Я становлюсь старше, или этот язык неуловим. Я (извините) добавил еще один ответ ниже, используя фактические имена переменных WDI. Если я могу, и если вы хотите включить его в свой, я удалю его. - person CPBL; 03.05.2013
comment
В документации есть несколько примеров стека/распаковки pandas.pydata.org/pandas-docs/stable/ (на этой же странице также показаны демонстрация поворота и плавления). Дополнительный ответ — это нормально. - person Wouter Overmeire; 03.05.2013

Сгруппируйте DataFrame по country и countrycode, а затем примените собственную функцию:

In [13]: def f(df):
   ....:     del df['country']
   ....:     del df['countrycode']
   ....:     df = df.set_index('varname')
   ....:     df.index.name = None
   ....:     df = df.T
   ....:     df.index.name = 'year'
   ....:     return df
   ....: 

In [14]: df.groupby(['country', 'countrycode']).apply(f).reset_index()
Out[14]: 
       country countrycode  year  GDP  Gini  educ  pop 
0  afghanistan         AFG  1960  5.6  0.77   8.1  888 
1  afghanistan         AFG  1961  5.7  0.78   8.2  889 
2  afghanistan         AFG  1962  5.8  0.75   8.3  890 
3      albania         ALB  1960  6.6  0.45   6.2  777 
4      albania         ALB  1961  6.7  0.46   6.3  778 
5      albania         ALB  1962  6.8  0.47   6.4  779 
person waitingkuo    schedule 03.05.2013
comment
Тоже умница. И тонкий. Спасибо. - person CPBL; 03.05.2013

Я предлагаю, чтобы @Wouter мог поместить это в свой (принятый) ответ, поскольку он использует фактические имена из данных WDI и делает его более вырезаемым и вставляемым для кого-то другого, использующего их. Извините, я уверен, что это неправильный способ сообщить об этом...

Для любых переменных, которые вы хотите сохранить/использовать, просто дайте им имя в этом словаре:

WDIconversions={"Year":'year',
"YearCode":'',
"Country Name":'country_name_wb',
"Country Code":'countryCode_ISO3_WB',
"Inflation, consumer prices (annual %)":'',
"Inflation, GDP deflator (annual %)":'',
"GDP per capita, PPP (constant 2005 international $)":'GDPpc',
"Firms with female participation in ownership (% of firms)":'',
"Investment in energy with private participation (current US$)":'',
"Investment in telecoms with private participation (current US$)":'',
"Investment in transport with private participation (current US$)":'',
"Investment in water and sanitation with private participation (current US$)":'',
"Labor participation rate, female (% of female population ages 15+)":'',
"Labor participation rate, male (% of male population ages 15+)":'',
"Labor participation rate, total (% of total population ages 15+)":'',
"Ratio of female to male labor participation rate (%)":'',
"Life expectancy at birth, female (years)":'',
"Life expectancy at birth, male (years)":'',
"Life expectancy at birth, total (years)":'lifeExpectancy',
"Population, total":'nat_pop',
"GINI index":'GiniWB',
} # etc etc etc 
dfW=pd.read_table(WBDrawfile) 
df = dfW.set_index(['Country Name','Country Code','Indicator Name'])
del df['Indicator Code']
df.columns.name = 'year'
df=df.stack().unstack('Indicator Name')
df=df[[kk for kk,ii in WDIconversions.items() if ii and kk in df]].reset_index().rename(columns=WDIconversions)

Это приводит к:

 df
<class 'pandas.core.frame.DataFrame'>
Int64Index: 12983 entries, 0 to 12982
Data columns:
country_name_wb        12983  non-null values
countryCode_ISO3_WB    12983  non-null values
year                   12983  non-null values
GiniWB                 845  non-null values
nat_pop                12601  non-null values
GDPpc                  6292  non-null values
educPrimary            4949  non-null values
lifeExpectancy         11077  non-null values
dtypes: float64(5), object(3)
person CPBL    schedule 03.05.2013