Related
I have a df below:
Country Product Value 11/01/1998 12/01/1998 01/01/1999 ... 07/01/2022 08/01/2022 09/01/2022 10/01/2022 11/01/2022 12/01/2022
0 France NaN Market 3330 7478 2273 ... NaN NaN NaN NaN NaN NaT
1 France NaN World 362 798 306 ... NaN NaN NaN NaN NaN NaT
3 Germany NaN Market 1452 2025 1314 ... NaN NaN NaN NaN NaN NaT
4 Germany NaN World 209 246 182 ... NaN NaN NaN NaN NaN NaT
6 Spain NaN Market 1943 2941 1426 ... NaN NaN NaN NaN NaN NaT
.. ... ... ... ... ... ... ... ... ... ... ... ... ...
343 Serbia and Montenegro 0 World 0 0 0 ... NaN NaN NaN NaN NaN NaT
345 Slovenia 0 Market 26 24 20 ... NaN NaN NaN NaN NaN NaT
346 Slovenia 0 World 0 0 1 ... NaN NaN NaN NaN NaN NaT
348 Slovakia 0 Market 2 2 0 ... NaN NaN NaN NaN NaN NaT
349 Slovakia 0 World 1 1 0 ... NaN NaN NaN NaN NaN NaT
I'm trying to rearrange the data and I figure that I need some sort of combination between transpose, melt, and/or stack. I've read through the documentation, but I can't seem to make sense of it. All combinations that I have tried haven't been able to give me what I need.
Columns should be: Country, Product, Market, World, Date (transpose the dates), and then the values should be under the Market or World Columns.
Any ideas?
Thanks so much and let me know if I can provide more information.
IIUC you need a combination of melt, set_index and unstack:
print (df.melt(id_vars=["Country", "Product", "Value"])
.set_index(["Country", "Product", "Value", "variable"])
.unstack("Value").reset_index())
Country Product variable value
Value Market World
0 France NaN 01/01/1999 2273 306
1 France NaN 07/01/2022 NaN NaN
2 France NaN 08/01/2022 NaN NaN
3 France NaN 09/01/2022 NaN NaN
4 France NaN 10/01/2022 NaN NaN
5 France NaN 11/01/1998 3330 362
6 France NaN 11/01/2022 NaN NaN
7 France NaN 12/01/1998 7478 798
8 France NaN 12/01/2022 NaT NaT
9 Germany NaN 01/01/1999 1314 182
10 Germany NaN 07/01/2022 NaN NaN
11 Germany NaN 08/01/2022 NaN NaN
12 Germany NaN 09/01/2022 NaN NaN
13 Germany NaN 10/01/2022 NaN NaN
14 Germany NaN 11/01/1998 1452 209
15 Germany NaN 11/01/2022 NaN NaN
16 Germany NaN 12/01/1998 2025 246
17 Germany NaN 12/01/2022 NaT NaT
18 Serbia and Montenegro 0.0 01/01/1999 NaN 0
19 Serbia and Montenegro 0.0 07/01/2022 NaN NaN
20 Serbia and Montenegro 0.0 08/01/2022 NaN NaN
21 Serbia and Montenegro 0.0 09/01/2022 NaN NaN
22 Serbia and Montenegro 0.0 10/01/2022 NaN NaN
23 Serbia and Montenegro 0.0 11/01/1998 NaN 0
24 Serbia and Montenegro 0.0 11/01/2022 NaN NaN
25 Serbia and Montenegro 0.0 12/01/1998 NaN 0
26 Serbia and Montenegro 0.0 12/01/2022 NaN NaT
27 Slovakia 0.0 01/01/1999 0 0
28 Slovakia 0.0 07/01/2022 NaN NaN
29 Slovakia 0.0 08/01/2022 NaN NaN
30 Slovakia 0.0 09/01/2022 NaN NaN
31 Slovakia 0.0 10/01/2022 NaN NaN
32 Slovakia 0.0 11/01/1998 2 1
33 Slovakia 0.0 11/01/2022 NaN NaN
34 Slovakia 0.0 12/01/1998 2 1
35 Slovakia 0.0 12/01/2022 NaT NaT
36 Slovenia 0.0 01/01/1999 20 1
37 Slovenia 0.0 07/01/2022 NaN NaN
38 Slovenia 0.0 08/01/2022 NaN NaN
39 Slovenia 0.0 09/01/2022 NaN NaN
40 Slovenia 0.0 10/01/2022 NaN NaN
41 Slovenia 0.0 11/01/1998 26 0
42 Slovenia 0.0 11/01/2022 NaN NaN
43 Slovenia 0.0 12/01/1998 24 0
44 Slovenia 0.0 12/01/2022 NaT NaT
45 Spain NaN 01/01/1999 1426 NaN
46 Spain NaN 07/01/2022 NaN NaN
47 Spain NaN 08/01/2022 NaN NaN
48 Spain NaN 09/01/2022 NaN NaN
49 Spain NaN 10/01/2022 NaN NaN
50 Spain NaN 11/01/1998 1943 NaN
51 Spain NaN 11/01/2022 NaN NaN
52 Spain NaN 12/01/1998 2941 NaN
53 Spain NaN 12/01/2022 NaT NaN
My initial df is:
ordinal id_easy latitude longitude
1 141 45.0714 7.6187
2 141 45.0739 7.6195
...
After applying Pivot table
my it looks like:
latitude ... longitude
ordinal 1 2 3 4 5 6 7 8 9 10 ... 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682
id_easy
141 45.1024 45.1027 45.1072 45.1076 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
403 45.0595 45.0595 45.0564 45.0533 45.0501 45.0468 45.0432 45.0356 45.0264 45.0174 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
920 45.0695 44.9727 44.9726 44.9566 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN
Everything is almost OK, but here I have first latitude values and then longitude values. Can I do something to make it 1 by 1? for each 'id_easy' - latitude and longitude.
I believe you need DataFrame.sort_index by second level:
df = df.sort_index(axis=1, level=1)
Let's say I have a simple Pandas DataFrame where one column contains a country name and another column contains some value. For example:
# Import Python Libraries
import numpy as np
import pandas as pd
# Create Sample DataFrame
df = pd.DataFrame(data={'Country': ['United States','United States','United States','United States', \
'United States','United States','United States','United States', \
'United States','United States','United States','United States', \
'Canada','Canada','Canada','Canada','Canada','Canada','Mexico', \
'Mexico','Mexico','Mexico','England','England','England','England', \
'England','England','England','England','England','England','England', \
'England','England','England','France','France','France','Spain','Germany', \
'Germany','Germany','Germany','Germany','Germany','Germany','Germany', \
'Germany','Germany'], 'Value': np.random.randint(1000, size=50)})
Which generates:
print(df.head())
Index Country Value
0 United States 943
1 United States 567
2 United States 534
3 United States 700
4 United States 470
My question is, what is the easiest way in Python to convert this DataFrame into one where each country has its own column and all the values of that country are listed in that column? In other words, how can I easily create a DataFrame where the number of columns is the unique count of countries in the 'Country' column, and that each column's length will vary depending on the number of times the corresponding country appears in the original DataFrame?
Here is sample code that provides a solution:
# Store Unique Country Names in Variable
columns = df['Country'].unique()
# Create Individual Country DataFrames
df_0 = df[df['Country'] == columns[0]]['Value'].values.tolist()
df_1 = df[df['Country'] == columns[1]]['Value'].values.tolist()
df_2 = df[df['Country'] == columns[2]]['Value'].values.tolist()
df_3 = df[df['Country'] == columns[3]]['Value'].values.tolist()
df_4 = df[df['Country'] == columns[4]]['Value'].values.tolist()
df_5 = df[df['Country'] == columns[5]]['Value'].values.tolist()
df_6 = df[df['Country'] == columns[6]]['Value'].values.tolist()
# Create Desired Output DataFrame
data_dict = {columns[0]: df_0, columns[1]: df_1, columns[2]: df_2, columns[3]: df_3, columns[4]: df_4, columns[5]: df_5, columns[6]: df_6}
new_df = pd.DataFrame({k:pd.Series(v[:len(df)]) for k,v in data_dict.items()})
Which generates:
print(new_df)
United States Canada Mexico England France Spain Germany
0 838.0 135.0 496.0 568.0 71.0 588.0 811.0
1 57.0 118.0 268.0 716.0 422.0 NaN 107.0
2 953.0 396.0 850.0 860.0 707.0 NaN 318.0
3 251.0 294.0 815.0 888.0 NaN NaN 633.0
4 127.0 466.0 NaN 869.0 NaN NaN 910.0
5 892.0 824.0 NaN 776.0 NaN NaN 472.0
6 11.0 NaN NaN 508.0 NaN NaN 466.0
7 563.0 NaN NaN 299.0 NaN NaN 200.0
8 864.0 NaN NaN 568.0 NaN NaN 637.0
9 810.0 NaN NaN 78.0 NaN NaN 392.0
10 268.0 NaN NaN 106.0 NaN NaN NaN
11 389.0 NaN NaN 153.0 NaN NaN NaN
12 NaN NaN NaN 217.0 NaN NaN NaN
13 NaN NaN NaN 941.0 NaN NaN NaN
While the above code works, it's obviously not a tenable solution for larger data sets. What is the most efficient way of generating this result from the original DataFrame?
Thank you!
Probably not the most performant solution out there, but it will get everything top justified.
df1 = df.groupby('Country').Value.agg(list).apply(pd.Series).T
df1.columns.name=None
Output: df1
Canada England France Germany Mexico Spain United States
0 653.0 187.0 396.0 491.0 251.0 433.0 919.0
1 215.0 301.0 25.0 107.0 755.0 NaN 435.0
2 709.0 581.0 858.0 691.0 158.0 NaN 166.0
3 626.0 706.0 NaN 572.0 767.0 NaN 352.0
4 516.0 999.0 NaN 393.0 NaN NaN 906.0
5 847.0 688.0 NaN 780.0 NaN NaN 489.0
6 NaN 722.0 NaN 19.0 NaN NaN 322.0
7 NaN 728.0 NaN 166.0 NaN NaN 753.0
8 NaN 765.0 NaN 299.0 NaN NaN 155.0
9 NaN 956.0 NaN 449.0 NaN NaN 438.0
10 NaN 41.0 NaN NaN NaN NaN 588.0
11 NaN 43.0 NaN NaN NaN NaN 796.0
12 NaN 485.0 NaN NaN NaN NaN NaN
13 NaN 218.0 NaN NaN NaN NaN NaN
The other option is to make use of Coldspeed's justify function and Yuca's pivot output:
import numpy as np
df2 = df.pivot(index=None, columns='Country', values='Value')
df2 = pd.DataFrame(
justify(df2.values, invalid_val=np.NaN, axis=0, side='up'),
columns=df2.columns
).dropna(0, 'all')
df2.columns.name=None
Output: df2
Canada England France Germany Mexico Spain United States
0 653 187 396 491 251 433 919
1 215 301 25 107 755 NaN 435
2 709 581 858 691 158 NaN 166
3 626 706 NaN 572 767 NaN 352
4 516 999 NaN 393 NaN NaN 906
5 847 688 NaN 780 NaN NaN 489
6 NaN 722 NaN 19 NaN NaN 322
7 NaN 728 NaN 166 NaN NaN 753
8 NaN 765 NaN 299 NaN NaN 155
9 NaN 956 NaN 449 NaN NaN 438
10 NaN 41 NaN NaN NaN NaN 588
11 NaN 43 NaN NaN NaN NaN 796
12 NaN 485 NaN NaN NaN NaN NaN
13 NaN 218 NaN NaN NaN NaN NaN
Use groupby, cumcount, and unstack with T:
df.set_index(['Country',df.groupby('Country').cumcount()])['Value'].unstack().T
Output:
Country Canada England France Germany Mexico Spain United States
0 535.0 666.0 545.0 522.0 581.0 525.0 394.0
1 917.0 130.0 76.0 882.0 563.0 NaN 936.0
2 344.0 376.0 960.0 442.0 247.0 NaN 819.0
3 760.0 272.0 NaN 604.0 976.0 NaN 975.0
4 745.0 199.0 NaN 512.0 NaN NaN 123.0
5 654.0 102.0 NaN 114.0 NaN NaN 690.0
6 NaN 570.0 NaN 318.0 NaN NaN 568.0
7 NaN 807.0 NaN 523.0 NaN NaN 385.0
8 NaN 18.0 NaN 890.0 NaN NaN 451.0
9 NaN 26.0 NaN 635.0 NaN NaN 282.0
10 NaN 871.0 NaN NaN NaN NaN 771.0
11 NaN 122.0 NaN NaN NaN NaN 505.0
12 NaN 0.0 NaN NaN NaN NaN NaN
13 NaN 578.0 NaN NaN NaN NaN NaN
pd.pivot takes you halfway there, the issue here is that your index have no information so your non NaN values are not at the top of the df
df.pivot(index=None, columns='Country', values = 'Value')
Country Canada England France ... Mexico Spain United States
0 NaN NaN NaN ... NaN NaN 992.0
1 NaN NaN NaN ... NaN NaN 814.0
2 NaN NaN NaN ... NaN NaN 489.0
3 NaN NaN NaN ... NaN NaN 943.0
4 NaN NaN NaN ... NaN NaN 574.0
5 NaN NaN NaN ... NaN NaN 428.0
6 NaN NaN NaN ... NaN NaN 907.0
7 NaN NaN NaN ... NaN NaN 899.0
8 NaN NaN NaN ... NaN NaN 379.0
9 NaN NaN NaN ... NaN NaN 130.0
Changing the original values in the df
I have a df as shown below, which I obtain after performing a number of calculations:
Acc Ep Direction Ttest_t Ttest_s T_count TPNL TS TotalPNL TotalS
A KA B -10.62 -0.21 3 -625.52 14.59 -667.61 24.28
B EF B -4.25 2.63 2 -448.08 26.88 -448.08 26.88
D SE B -3.94 8.63 4 -533.70 75.41 -550.26 128.38
G UA S -6.85 -0.09 3 -563.83 19.58 -411.06 21.54
N EL B -5.39 2.84 2 -2230.23 464.56 -6641.1 1232.79
N SD B -4.70 -0.21 2 -1057.0 117.45 -6641.1 1232.79
S UD B -5.48 0.18 33 1416.69 3981.32 955.34 4475.32
then I use the MultiIndex function as follows:
columns = [('Index','Acc'), ('Index','Ep'), ('EPNL','Ttest_t'), ('EPNL','TPNL'), ('EPNL','TotalPNL'), ('SPaid','Ttest_s'), ('SPaid','TS'), ('SPaid','TotalS'), ('Other','Direction'), ('Other','T_count')]
temp3.columns=pd.MultiIndex.from_tuples(columns)
This does gives me the table format I want. but, it adds null values to my table(as shown below)
Index EPNL SPaid Other O
Acc Epic Ttest_t TPNL TotalPNL Ttest_s TS TotalS Direction t
NaN NaN NaN NaN NaN NaN NaN NaN NaN h
NaN NaN NaN NaN NaN NaN NaN NaN NaN e
NaN NaN NaN NaN NaN NaN NaN NaN NaN r
NaN NaN NaN NaN NaN NaN NaN NaN NaN T
NaN NaN NaN NaN NaN NaN NaN NaN NaN r
NaN NaN NaN NaN NaN NaN NaN NaN NaN a
NaN NaN NaN NaN NaN NaN NaN NaN NaN d
NaN NaN NaN NaN NaN NaN NaN NaN NaN e
NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN c
NaN NaN NaN NaN NaN NaN NaN NaN NaN o
NaN NaN NaN NaN NaN NaN NaN NaN NaN u
NaN NaN NaN NaN NaN NaN NaN NaN NaN n
NaN NaN NaN NaN NaN NaN NaN NaN NaN t
A KA B -10.62 -0.21 3 -625.52 14.59 -667.61 24.28
B EF B -4.25 2.63 2 -448.08 26.88 -448.08 26.88
D SE B -3.94 8.63 4 -533.70 75.41 -550.26 128.38
G UA S -6.85 -0.09 3 -563.83 19.58 -411.06 21.54
N EL B -5.39 2.84 2 -2230.23 464.56 -6641.17 1232.79
N SD B -4.70 -0.21 2 -1057.02 117.45 -6641.17 1232.79
S UD B -5.48 0.18 33 1416.69 3981.32 955.34 4475.32
Any ideas on why it is doing that. I prefer it not adding the values(I don't want to use dropna)
Because string is iterable, ('Other' 'T_count') is converted to
'O','t','h','e',r',' ','T','_','c','o','u','n','t'
and created 16 level MultiIndex.
Solution is add , like ('Other', 'T_count').
columns = [('Index','Acc'), ('Index','Ep'),
('EPNL','Ttest_t'), ('EPNL','TPNL'),
('EPNL','TotalPNL'), ('SPaid','Ttest_s'),
('SPaid','TS'), ('SPaid','TotalS'),
('Other','Direction'), ('Other','T_count')]
temp3.columns=pd.MultiIndex.from_tuples(columns)
print (temp3)
Index EPNL SPaid Other \
Acc Ep Ttest_t TPNL TotalPNL Ttest_s TS TotalS Direction
0 A KA B -10.62 -0.21 3 -625.52 14.59 -667.61
1 B EF B -4.25 2.63 2 -448.08 26.88 -448.08
2 D SE B -3.94 8.63 4 -533.70 75.41 -550.26
3 G UA S -6.85 -0.09 3 -563.83 19.58 -411.06
4 N EL B -5.39 2.84 2 -2230.23 464.56 -6641.10
5 N SD B -4.70 -0.21 2 -1057.00 117.45 -6641.10
6 S UD B -5.48 0.18 33 1416.69 3981.32 955.34
T_count
0 24.28
1 26.88
2 128.38
3 21.54
4 1232.79
5 1232.79
6 4475.32
I am trying to figure out how to do merge. I have a labels.csv which contains the names that I have to use to replace the numbers for the same field in my dat.csv
My dat.csv is as follows:
Id,Help in household,Maths,Reading,Science,Social
11011001001,4,20.37,,27.78,
11011001002,3,12.96,,38.18,
11011001003,4,27.78,70,,
11011001004,4,,56.67,,36
11011001005,1,,,14.55,8.33
11011001006,4,,23.33,,30
11011001007,4,40.74,70,,
11011001008,3,,26.67,,22.92
11011001009,2,24.07,,25.45,
11011001010,4,18.52,26.67,,
11011001012,2,37.04,16.67,,
11011001013,4,20.37,,20,
11011001014,2,,,29.63,35.42
11011001015,4,27.78,66.67,,
11011001016,0,18.52,,,
11011001017,4,,,42.59,32
11011001018,2,16.67,,,
11011001019,3,,,21.82,
11011001020,4,,20,,16
11011001021,1,,,18.52,16.67
My labels.csv is as follows:
Column,Name,Level,Rename
Help in household,Every day,4,Every day
Help in household,Never,1,Never
Help in household,Once a month,2,Once a month
Help in household,Once a week,3,Once a week
my programme is as follows:
import pandas as pd
df = pd.read_csv('dat.csv')
labels = pd.read_csv('labels.csv')
df=df.merge(labels,left_on='Help in household',right_on='Name',how='left')
print df
However, the names do not appear as I want them to.
STUID Help in household Maths % Reading % Science % Social % \
0 11011001001 4 20.37 NaN 27.78 NaN
1 11011001002 3 12.96 NaN 38.18 NaN
2 11011001003 4 27.78 70.00 NaN NaN
3 11011001004 4 NaN 56.67 NaN 36.00
4 11011001005 1 NaN NaN 14.55 8.33
5 11011001006 4 NaN 23.33 NaN 30.00
6 11011001007 4 40.74 70.00 NaN NaN
7 11011001008 3 NaN 26.67 NaN 22.92
8 11011001009 2 24.07 NaN 25.45 NaN
9 11011001010 4 18.52 26.67 NaN NaN
10 11011001012 2 37.04 16.67 NaN NaN
11 11011001013 4 20.37 NaN 20.00 NaN
12 11011001014 2 NaN NaN 29.63 35.42
13 11011001015 4 27.78 66.67 NaN NaN
14 11011001016 0 18.52 NaN NaN NaN
15 11011001017 4 NaN NaN 42.59 32.00
16 11011001018 2 16.67 NaN NaN NaN
17 11011001019 3 NaN NaN 21.82 NaN
18 11011001020 4 NaN 20.00 NaN 16.00
19 11011001021 1 NaN NaN 18.52 16.67
Column Name Level Rename
0 NaN NaN NaN NaN
1 NaN NaN NaN NaN
2 NaN NaN NaN NaN
3 NaN NaN NaN NaN
4 NaN NaN NaN NaN
5 NaN NaN NaN NaN
6 NaN NaN NaN NaN
7 NaN NaN NaN NaN
8 NaN NaN NaN NaN
9 NaN NaN NaN NaN
10 NaN NaN NaN NaN
11 NaN NaN NaN NaN
12 NaN NaN NaN NaN
13 NaN NaN NaN NaN
14 NaN NaN NaN NaN
15 NaN NaN NaN NaN
16 NaN NaN NaN NaN
17 NaN NaN NaN NaN
18 NaN NaN NaN NaN
19 NaN NaN NaN NaN
What am I doing wrong?
Okay, is this what you want?
df['Name'] = df['Help in household'].map(labels.set_index('Level')['Name'])
Output:
Id Help in household Maths Reading Science Social \
0 11011001001 4 20.37 NaN 27.78 NaN
1 11011001002 3 12.96 NaN 38.18 NaN
2 11011001003 4 27.78 70.00 NaN NaN
3 11011001004 4 NaN 56.67 NaN 36.00
4 11011001005 1 NaN NaN 14.55 8.33
5 11011001006 4 NaN 23.33 NaN 30.00
6 11011001007 4 40.74 70.00 NaN NaN
7 11011001008 3 NaN 26.67 NaN 22.92
8 11011001009 2 24.07 NaN 25.45 NaN
9 11011001010 4 18.52 26.67 NaN NaN
10 11011001012 2 37.04 16.67 NaN NaN
11 11011001013 4 20.37 NaN 20.00 NaN
12 11011001014 2 NaN NaN 29.63 35.42
13 11011001015 4 27.78 66.67 NaN NaN
14 11011001016 0 18.52 NaN NaN NaN
15 11011001017 4 NaN NaN 42.59 32.00
16 11011001018 2 16.67 NaN NaN NaN
17 11011001019 3 NaN NaN 21.82 NaN
18 11011001020 4 NaN 20.00 NaN 16.00
19 11011001021 1 NaN NaN 18.52 16.67
Name
0 Every day
1 Once a week
2 Every day
3 Every day
4 Never
5 Every day
6 Every day
7 Once a week
8 Once a month
9 Every day
10 Once a month
11 Every day
12 Once a month
13 Every day
14 NaN
15 Every day
16 Once a month
17 Once a week
18 Every day
19 Never