Pandas Match row to column values - python
I have a JSON output which I am trying to get in excel.
What I am trying to do is match the WEIGHT as column Header.
I could get this output using some loops.
What I am trying to get is have all the Weights as the first column header and if it has values paste it in else NaN.
Desired Output:
page = requests.get(mainurl)
data = json.loads(page.text)
for i in data['categories']:
for j in i['items']:
if a == 1: # so changes and appends keys per category (highlighted)
a=2 # so not true in this loop
s=tuple(j['prices'].keys())
ws.append(s)
PVAL=list(j['prices'].values())
ws.append(PVAL)# append the value
a=1 # makes true next category
p= []
for i in price: # I know this is absolute madness but dicts were getting sorted
i = str(i).replace("'",'').replace('{','').replace('}','')# get price values
p.append(i)
###apppend in excel
Note : As you can tell by the above code, I am a complete Beginner. And the above code could have been pretty with 2-3 lines of Pandas :(
I am now tinkering with Pandas to do it since I think it will be faster and better.
JsonOutput
MAJOR EDIT:
So I didn't have much time so I did this:
for i in data['categories']:
for j in i['items']:
PVAL=j['prices']
try:
ounce = PVAL['ounce']
except:
ounce = 'NaN'
try:
gram = PVAL['gram']
except:gram = 'NaN'
try:
twograms = PVAL['two_grams']
except:twograms='NaN'
try:
quarter=PVAL['quarter']
except:quarter='NaN'
try:
eighth=PVAL['eighth']
except:eighth='NaN'
try:
halfO=PVAL['half_ounce']
except:halfO='NaN'
try:
unit = PVAL['unit']
except:unit='NaN'
try:
halfgram = PVAL['half_gram']
except:halfgram='NaN'
name= j['name']
cat = j['category_name']
listname = j['listing_name']
namel.append(name)
catl.append(cat)
listnamel.append(listname)
halfOl.append(halfO)
halfgraml.append(halfgram)
unitl.append(unit)
eighthl.append(eighth)
twogramsl.append(twograms)
quarterl.append(quarter)
ouncel.append(ounce)
graml.append(gram)
Then these lists are appended in Excel.
I know it is not Pythonic but I am still trying to findout a good way to do it in Pandas.
as my rep is still low, cannot post any comment yet, thus will just post it here and will edit this if further clarifications are provided.
I don't see any WEIGHTs in the desired output. If I understand the json file correctly, you are iterating prices given a weight unit. Is the expected output to loop over each item and iterate over prices per weight unit. Put NaN if weight unit is not available. Is there a list of possible weight units?
Pandas also has read_json function and thus can directly load this to a Pandas dataframe.
-- edited ---
Apologies for the delay. Please see below answer
import pandas as pd
import json
from cytoolz.dicttoolz import merge
#replace below with your json loader
with open('sample.json') as json_dta:
dict_dta = json.load(json_dta)
list_columns = ['id', 'name', 'category_name', 'ounce', 'gram', 'two_grams', 'quarter', 'eighth','half_ounce','unit','half_gram']
df = pd.io.json.json_normalize(dict_dta, ['categories', ['items']]).pipe(lambda x: x.drop('prices', 1).join(x.prices.apply(lambda y: pd.Series(merge(y)))))[list_columns]
Above will result to:
id name category_name ounce gram two_grams quarter eighth half_ounce unit half_gram
0 10501503 Recon Indica 99.0 9.0 0.0 40.0 25.0 70.0 NaN NaN
1 11614583 Kush Dawg Indica 99.0 9.0 0.0 40.0 25.0 70.0 NaN NaN
2 8602219 OG Kush Indica 99.0 9.0 0.0 40.0 25.0 70.0 NaN NaN
3 11448858 Poison OG Outdoor Sativa 69.0 9.0 0.0 40.0 25.0 50.0 NaN NaN
4 11731126 SunBurn 2.0 Outdoor Sativa 69.0 0.0 0.0 0.0 0.0 0.0 NaN NaN
5 6412418 Poison OG Sativa 99.0 9.0 18.0 40.0 25.0 70.0 NaN NaN
6 8982466 Sativa Trim Sativa 30.0 0.0 0.0 0.0 0.0 15.0 NaN NaN
7 11545434 Chupacabra Outdoor Hybrid 69.0 9.0 0.0 40.0 25.0 50.0 NaN NaN
8 11458944 Platinum Girl Scout Cookies Outdoor Hybrid 69.0 9.0 0.0 40.0 25.0 50.0 NaN NaN
9 11296163 Bubblegum Hybrid 99.0 9.0 0.0 40.0 25.0 70.0 NaN NaN
10 11614623 C4 Hybrid 99.0 9.0 0.0 40.0 25.0 70.0 NaN NaN
11 11333124 Chem Dawg Outdoor Hybrid 69.0 9.0 0.0 40.0 25.0 50.0 NaN NaN
12 11458988 Candy Kush Hybrid 99.0 9.0 0.0 40.0 25.0 70.0 NaN NaN
13 10501592 Candy Kush Outdoor Hybrid 69.0 9.0 0.0 40.0 25.0 50.0 NaN NaN
14 9123804 ZOOTROCKS LemonGrass Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
15 9412336 Cherry Limeade 100mg Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
16 4970503 Peanut Budda Buddha, 100mg REC Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
17 9412238 Golden Strawberry Puck 100mg REC - CO Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
18 9412232 Cherry Puck 100mg REC - CO Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
19 9412228 Assorted Sour Pucks 100mg REC - CO Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
20 6454686 Assorted Fruity Pucks 100mg REC - CO Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
21 9412295 Sour Gummies Sativa 100mg, Recreational Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
22 7494303 Cheeba Chews Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
23 9411974 Mile High Mint, 100mg REC Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
24 9411972 Boulder Bar, 100mg Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
25 9412286 Sour Gummies Indica 100mg, Recreational Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
26 9412242 Watermelon Puck 100mg - REC Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
27 10066310 Coffee & Doughnuts Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
28 10065124 Wildflower Honey Edible NaN NaN NaN NaN NaN NaN 24.0 NaN
29 10064962 Clover Honey Edible NaN NaN NaN NaN NaN NaN 24.0 NaN
30 9412290 Sour Gummies Peach 100mg, Recreational Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
31 5926966 Stratos 100mg Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
32 10066271 Salt & Nibs Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
33 10065225 Yampa Valley Honey Edible NaN NaN NaN NaN NaN NaN 24.0 NaN
34 9412873 Fruit Punch Mints 100mg Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
35 9412251 Sour Gummies Hybrid 100mg, Recreational Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
36 9412922 Dutch Girl Carmel Waffle, 100mg Edible NaN NaN NaN NaN NaN NaN 20.0 NaN
37 6790292 Hybrid Distillate Jar Concentrate NaN 36.0 0.0 NaN NaN NaN NaN 0.0
38 6379060 Hybrid Cartridge Concentrate NaN 25.0 0.0 NaN NaN NaN NaN 18.0
39 9009149 Pure Cannabis Oil Hybrid Concentrate NaN 25.0 0.0 NaN NaN NaN NaN 0.0
40 9400145 Pure Cannabis Oil Sativa Concentrate NaN 25.0 0.0 NaN NaN NaN NaN 0.0
41 9409961 Sativa Cartridge Concentrate NaN 25.0 0.0 NaN NaN NaN NaN 18.0
42 9400121 Pure Cannabis Oil Indica Concentrate NaN 25.0 0.0 NaN NaN NaN NaN 0.0
43 9409954 Indica Cartridge Concentrate NaN 25.0 0.0 NaN NaN NaN NaN 18.0
44 9400467 Indica Distillate Jar Concentrate NaN 36.0 0.0 NaN NaN NaN NaN 0.0
45 9691836 PWO Wax by Mahatma Concentrate NaN 25.0 0.0 NaN NaN NaN NaN 0.0
46 9409970 Sativa Distillate Jar Concentrate NaN 36.0 0.0 NaN NaN NaN NaN 0.0
47 6134675 Bongs Gear NaN NaN NaN NaN NaN NaN 40.0 NaN
48 5993354 Small Glass Pipes Gear NaN NaN NaN NaN NaN NaN 10.0 NaN
49 4393434 Large Glass Pipes Gear NaN NaN NaN NaN NaN NaN 20.0 NaN
50 5941409 Pain Relief Salve, 2oz Topicals NaN NaN NaN NaN NaN NaN 26.0 NaN
51 8768835 THC Pain Stick Topicals NaN NaN NaN NaN NaN NaN 20.0 NaN
52 6370279 FORIA Pleasure (30ml) Spray Bottle Topicals NaN NaN NaN NaN NaN NaN 55.0 NaN
53 8911546 Bath Soak Topicals NaN NaN NaN NaN NaN NaN 20.0 NaN
54 9123854 FORIA Relief (2-pack) Suppositories Topicals NaN NaN NaN NaN NaN NaN 24.0 NaN
55 4187102 1 Gram Strain Specific-Prerolls Preroll NaN NaN NaN NaN NaN NaN 9.0 NaN
Related
Major rearrangement of pandas DataFrame containing nested lists and dictionaries ( CFBD (College Football Database))
The College Football Database (cfbd) contains all team ranks for each week of every college football season going back to 1937.I am trying to set up data from the cfbd in a way that will allow me to examine how a particular team's AP Top 25 ranking changes week to week in multiple seasons. Ideally, I would like to organize this data like so: Team Season Week1 Rank Week2 Rank 16 more weeks worth of ranks ... Alabama 1937 4 3 etc etc Alabama 1938 3 6 etc etc Wyoming 2017 24 nr Wyoming 2020 nr 25 So the output table will have a row for every school that has ever been ranked for each year that it has been ranked. After running pip install cfbd I set up the API: import pandas as pd import cfbd configuration = cfbd.Configuration() # The cfbd API requires users to sign up for a free # private key in order to access the data. # https://collegefootballdata.com/key configuration.api_key['Authorization'] = '[MY_SECRET_KEY]' configuration.api_key_prefix['Authorization'] = 'Bearer' api_rankings = cfbd.RankingsApi(cfbd.ApiClient(configuration)) #Initialize blank dataframe allrankings_df = pd.DataFrame() #year is required in the API call, so I have to collect each year separately for yr in range(1936,2022): rankings = api_rankings.get_rankings(year = yr) #get the data rankings_df = pd.DataFrame.from_records([p.to_dict() for p in rankings]) allrankings_df2 = allrankings_df2.append(rankings_df, ignore_index=True) This gives me a dataframe structured like so: |season|season_type|week|List of poll objects| |------|-----------|----|-------------------| |1936|regular|1|object| Those poll objects have a Poll Name ("AP Top 25", "Coaches Poll") and ranking data. Like this, except with four or five different Polls. {'poll': 'Coaches Poll', 'ranks': [{'rank': 1, 'school': 'Alabama', 'conference': 'SEC', 'firstPlaceVotes': 44, 'points': 1601}, {'rank': 2, 'school': 'Clemson', 'conference': 'ACC', 'firstPlaceVotes': 14, 'points': 1536}, The API describes all of this like so: [ { "season": 0, "seasonType": "string", "week": 0, "polls": [ { "poll": "string", "ranks": [ { "rank": 0, "school": "string", "conference": "string", "firstPlaceVotes": 0, "points": 0 } ] } ] } ] Phew. I can sort of picture how to do this by iterating through every year & week & polling object and building a new table piece by piece. But I also have read many times that I shouldn't do that - that I should be vectorizing. I know at this point I should share what I have tried so far, but to be honest, I am nowhere close. Can anyone point me in the right direction? I am willing to bang my head on this, but I can't even tell how I should be banging. Do I need some dataframe methods like melt or ravel? Or should I be trying to set this up with Boolean dataframe referencing? references: https://api.collegefootballdata.com/api/docs/?url=/api-docs.json#/rankings/getRankings https://pypi.org/project/cfbd/
import pandas as pd import cfbd configuration = cfbd.Configuration() # The cfbd API requires users to sign up for a free # private key in order to access the data. # https://collegefootballdata.com/key configuration.api_key['Authorization'] = 'IXRIjbjaO/WQVVLk/Yj2tA1yB2K1K2ZprNcCmueKdHKMHbyst4fYD6lVbp1xeHXB' configuration.api_key_prefix['Authorization'] = 'Bearer' api_rankings = cfbd.RankingsApi(cfbd.ApiClient(configuration)) #Initialize blank dataframe allrankings_df = pd.DataFrame() #year is required in the API call, so I have to collect each year separately for yr in range(1936,2022): print(yr) rankings = api_rankings.get_rankings(year = yr) #get the data rankings = [p.to_dict() for p in rankings] # Flattens the json rankings_df = pd.json_normalize(rankings, record_path=['polls', 'ranks'], meta = ['season','week']) rankings_df = rankings_df.drop_duplicates(subset=['school','week'], keep='first') # Pivot so Week rankings are the columns rankings_df = rankings_df.pivot( index = ['school','season'], columns = 'week', values = 'rank').add_prefix('Week').add_suffix(' Rank').reset_index(drop=False) rankings_df = rankings_df.rename(columns={'school':'Team', 'season':'Season'}) allrankings_df = pd.concat([allrankings_df, rankings_df], axis=0) allrankings_df = allrankings_df.sort_values(['Team', 'Season']).reset_index(drop=True) Output: First 20 rows: print(allrankings_df.head(20).to_string()) week Team Season Week1 Rank Week2 Rank Week3 Rank Week4 Rank Week5 Rank Week6 Rank Week7 Rank Week8 Rank Week9 Rank Week10 Rank Week11 Rank Week12 Rank Week13 Rank Week14 Rank Week15 Rank Week16 Rank Week17 Rank 0 Abilene Christian 2012 10.0 9.0 20.0 19.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 1 Air Force 1958 NaN NaN NaN NaN NaN 14.0 13.0 10.0 10.0 9.0 8.0 NaN NaN NaN NaN NaN NaN 2 Air Force 1959 15.0 NaN 18.0 18.0 17.0 NaN NaN 18.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN 3 Air Force 1969 NaN NaN NaN NaN NaN 20.0 19.0 19.0 20.0 NaN NaN NaN NaN NaN NaN NaN NaN 4 Air Force 1970 NaN NaN 20.0 10.0 8.0 7.0 7.0 7.0 9.0 13.0 10.0 12.0 11.0 11.0 NaN NaN NaN 5 Air Force 1971 NaN NaN NaN NaN NaN NaN 20.0 18.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN 6 Air Force 1972 NaN NaN NaN NaN 19.0 16.0 16.0 NaN 19.0 NaN NaN NaN NaN NaN NaN NaN NaN 7 Air Force 1983 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 18.0 17.0 16.0 NaN NaN 8 Air Force 1985 NaN NaN NaN NaN 19.0 17.0 13.0 10.0 8.0 7.0 5.0 4.0 13.0 11.0 10.0 10.0 NaN 9 Air Force 1989 NaN NaN NaN NaN 24.0 20.0 17.0 19.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN 10 Air Force 1995 NaN NaN NaN 21.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 11 Air Force 1997 NaN NaN NaN NaN NaN NaN 23.0 19.0 18.0 NaN NaN NaN NaN NaN 24.0 23.0 23.0 12 Air Force 1998 NaN NaN NaN 23.0 NaN NaN NaN NaN NaN 25.0 23.0 20.0 18.0 17.0 16.0 NaN NaN 13 Air Force 1999 NaN NaN NaN NaN 24.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 14 Air Force 2002 NaN NaN NaN NaN NaN NaN 25.0 19.0 15.0 19.0 NaN NaN NaN NaN NaN NaN NaN 15 Air Force 2003 NaN NaN NaN NaN NaN NaN 25.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 16 Air Force 2010 NaN NaN NaN NaN NaN 25.0 23.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 17 Air Force 2019 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 25.0 24.0 NaN 18 Alabama 1936 NaN NaN 14.0 4.0 8.0 3.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 19 Alabama 1937 2.0 3.0 2.0 3.0 3.0 4.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN .... [3954 rows x 19 columns]
Pandas row filtering reproduces entire table and turns data into NaNs [duplicate]
This question already has answers here: How to change column names in pandas Dataframe using a list of names? (5 answers) Closed 3 years ago. I have a Pandas dataframe with several columns. I want to create a new dataframe which contains all the rows in the original dataframe for which the boolean value "Present" is True. Normally the way you are supposed to do this is by calling grades[grades['Present']], but I get the following unexpected result: It reproduces the entire dataframe, except changes the True values in the "Present" column to 1s (the False ones become NaNs). Any idea why this might be happening? Here is my full script: import pandas as pd # read CSV and clean up data grades = pd.read_csv("2학기 speaking test grades - 2·3학년.csv") grades = grades[["Year","Present?","내용 / 30","유찬성 / 40","태도 / 30"]] grades.columns = [["Year","Present","Content","Fluency","Attitude"]] # Change integer Present to a boolean grades['Present']=grades['Present']==1 print(grades.head()) print(grades.dtypes) print(grades[grades['Present']]) And terminal output: Year Present Content Fluency Attitude 0 2 True 30.0 40.0 30.0 1 2 True 30.0 40.0 30.0 2 2 True 30.0 40.0 30.0 3 2 True 30.0 40.0 30.0 4 2 True 30.0 40.0 30.0 Year int64 Present bool Content float64 Fluency float64 Attitude float64 dtype: object Year Present Content Fluency Attitude 0 NaN 1.0 NaN NaN NaN 1 NaN 1.0 NaN NaN NaN 2 NaN 1.0 NaN NaN NaN 3 NaN 1.0 NaN NaN NaN 4 NaN 1.0 NaN NaN NaN 5 NaN 1.0 NaN NaN NaN 6 NaN 1.0 NaN NaN NaN 7 NaN 1.0 NaN NaN NaN 8 NaN 1.0 NaN NaN NaN 9 NaN 1.0 NaN NaN NaN 10 NaN 1.0 NaN NaN NaN 11 NaN 1.0 NaN NaN NaN 12 NaN 1.0 NaN NaN NaN 13 NaN 1.0 NaN NaN NaN 14 NaN 1.0 NaN NaN NaN 15 NaN 1.0 NaN NaN NaN 16 NaN 1.0 NaN NaN NaN 17 NaN 1.0 NaN NaN NaN 18 NaN 1.0 NaN NaN NaN 19 NaN 1.0 NaN NaN NaN 20 NaN 1.0 NaN NaN NaN 21 NaN 1.0 NaN NaN NaN 22 NaN 1.0 NaN NaN NaN 23 NaN 1.0 NaN NaN NaN 24 NaN 1.0 NaN NaN NaN 25 NaN 1.0 NaN NaN NaN 26 NaN 1.0 NaN NaN NaN 27 NaN 1.0 NaN NaN NaN 28 NaN 1.0 NaN NaN NaN 29 NaN 1.0 NaN NaN NaN .. ... ... ... ... ... 91 NaN NaN NaN NaN NaN 92 NaN NaN NaN NaN NaN 93 NaN 1.0 NaN NaN NaN 94 NaN 1.0 NaN NaN NaN 95 NaN NaN NaN NaN NaN 96 NaN 1.0 NaN NaN NaN 97 NaN 1.0 NaN NaN NaN 98 NaN 1.0 NaN NaN NaN 99 NaN 1.0 NaN NaN NaN 100 NaN 1.0 NaN NaN NaN 101 NaN 1.0 NaN NaN NaN 102 NaN 1.0 NaN NaN NaN 103 NaN 1.0 NaN NaN NaN 104 NaN 1.0 NaN NaN NaN 105 NaN 1.0 NaN NaN NaN 106 NaN 1.0 NaN NaN NaN 107 NaN 1.0 NaN NaN NaN 108 NaN 1.0 NaN NaN NaN 109 NaN 1.0 NaN NaN NaN 110 NaN 1.0 NaN NaN NaN 111 NaN 1.0 NaN NaN NaN 112 NaN 1.0 NaN NaN NaN 113 NaN 1.0 NaN NaN NaN 114 NaN 1.0 NaN NaN NaN 115 NaN 1.0 NaN NaN NaN 116 NaN 1.0 NaN NaN NaN 117 NaN 1.0 NaN NaN NaN 118 NaN 1.0 NaN NaN NaN 119 NaN 1.0 NaN NaN NaN 120 NaN 1.0 NaN NaN NaN [121 rows x 5 columns] Here is the CSV file. SE won't let me upload it directly, so if you paste it into your own CSV file you'll need to modify the Python code above to specify that it's in the EUC-KR encoding, like so: pd.read_csv("paste.csv",encoding="EUC-KR") Year,Class,Year / class * presence (used to filter for averages),Present?,내용 / 30,유찬성 / 40,태도 / 30,Total,,,Averages (평균점),, 2,2,22,1,30,40,30,100,,,Grade distribution (점수 막대 그래프),, 2,2,22,1,30,40,30,100,,,The graph below includes the scores of all students in grades 2 and 3. ,, 2,2,22,1,30,40,30,100,,,아래 그래프에는 2·3학년에서 모든 학생의 점수가 정리됩니다.,, 2,2,22,1,30,40,30,100,,,,, 2,2,22,1,30,40,30,100,,,,, 2,1,21,1,30,40,30,100,,,,, 2,1,21,1,30,40,30,100,,,,, 2,1,21,1,30,40,30,100,,,,, 2,1,21,1,30,40,30,100,,,,, 2,1,21,1,30,40,30,100,,,,, 2,1,21,1,30,40,30,100,,,,, 3,2,32,1,30,40,30,100,,,,, 3,2,32,1,30,40,30,100,,,,, 3,2,32,1,30,40,30,100,,,,, 3,2,32,1,30,40,30,100,,,,, 3,2,32,1,30,40,30,100,,,,, 3,2,32,1,30,40,30,100,,,,, 3,2,32,1,30,40,30,100,,,,, 3,2,32,1,30,40,30,100,,,,, 2,2,22,1,30,30,30,90,,,,, 2,2,22,1,30,30,30,90,,,,, 2,2,22,1,30,30,30,90,,,,, 2,2,22,1,30,30,30,90,,,Average scores (평균점),, 2,2,22,1,30,30,30,90,,,These averages only count students who were present for the test.,, 2,2,22,1,30,30,30,90,,,평균점에는 참석한 학생의 점수만 포함됩니다.,, 2,2,22,1,30,30,30,90,,,,, 2,2,22,1,30,30,30,90,,,2학년 1반,,77.1 2,1,21,1,30,30,30,90,,,2학년 2반,,77.6 2,1,21,1,30,30,30,90,,,3학년 1반,,71.5 2,1,21,1,30,30,30,90,,,3학년 2반,,77.4 2,1,21,1,30,30,30,90,,,,, 2,1,21,1,30,30,30,90,,,,, 2,1,21,1,30,30,30,90,,,,, 2,1,21,1,30,30,30,90,,,,, 2,1,21,1,30,30,30,90,,,,, 2,1,21,1,30,30,30,90,,,,, 2,1,21,1,30,30,30,90,,,,, 2,1,21,1,30,30,30,90,,,,, 3,2,32,1,30,30,30,90,,,,, 3,2,32,1,30,30,30,90,,,,, 3,2,32,1,30,30,30,90,,,,, 3,2,32,1,30,30,30,90,,,,, 3,2,32,1,20,40,30,90,,,,, 2,2,22,1,20,30,30,80,,,,, 2,2,22,1,20,30,30,80,,,,, 2,2,22,1,30,20,30,80,,,,, 2,2,22,1,30,20,30,80,,,,, 2,2,22,1,30,30,20,80,,,,, 2,2,22,1,30,20,30,80,,,,, 2,1,21,1,20,30,30,80,,,,, 2,1,21,1,20,30,30,80,,,,, 2,1,21,1,30,30,20,80,,,,, 3,2,32,1,20,30,30,80,,,,, 3,2,32,1,30,20,30,80,,,,, 3,2,32,1,20,30,30,80,,,,, 3,2,32,1,30,30,20,80,,,,, 3,2,32,1,30,20,30,80,,,,, 2,2,22,1,10,30,30,70,,,,, 2,2,22,1,20,20,30,70,,,,, 2,2,22,1,30,20,20,70,,,,, 2,2,22,1,20,20,30,70,,,,, 2,2,22,1,20,20,30,70,,,,, 3,2,32,1,30,10,30,70,,,,, 3,2,32,1,20,30,20,70,,,,, 3,2,32,1,20,20,30,70,,,,, 2,1,21,1,20,20,20,60,,,,, 2,1,21,1,10,20,30,60,,,,, 2,2,22,1,10,20,20,50,,,,, 2,2,22,1,10,10,30,50,,,,, 2,1,21,1,10,10,30,50,,,,, 2,1,21,1,20,20,10,50,,,,, 3,2,32,1,10,10,30,50,,,,, 3,2,32,1,10,10,30,50,,,,, 2,2,22,1,10,0,30,40,,,,, 2,1,21,1,10,0,30,40,,,,, 3,2,32,1,10,0,30,40,,,,, 3,2,32,1,10,10,20,40,,,,, 2,2,22,1,0,0,30,30,,,,, 2,1,21,1,0,0,30,30,,,,, 2,1,21,1,0,0,30,30,,,,, 3,2,32,1,0,0,30,30,,,,, 3,2,32,1,0,0,20,20,,,,, 2,1,21,1,0,0,10,10,,,,, 2,2,22,1,0,0,30,30,,,,, 2,2,0,0,,,,0,,,,, 2,2,0,0,,,,0,,,,, 2,1,0,0,,,,0,,,,, 2,1,0,0,,,,0,,,,, 2,1,0,0,,,,0,,,,, 2,1,0,0,,,,0,,,,, 3,2,0,0,,,,0,,,,, 3,2,0,0,,,,0,,,,, 3,1,0,0,,,,0,,,,, 3,1,31,1,30,20,30,80,,,,, 3,1,31,1,0,0,30,30,,,,, 3,1,0,0,,,,0,,,,, 3,1,31,1,30,20,10,60,,,,, 3,1,31,1,30,30,30,90,,,,, 3,1,31,1,30,30,30,90,,,,, 3,1,31,1,20,20,20,60,,,,, 3,1,31,1,30,20,30,80,,,,, 3,1,31,1,30,40,30,100,,,,, 3,1,31,1,30,30,30,90,,,,, 3,1,31,1,30,30,30,90,,,,, 3,1,31,1,30,30,30,90,,,,, 3,1,31,1,20,30,20,70,,,,, 3,1,31,1,30,30,30,90,,,,, 3,1,31,1,30,40,30,100,,,,, 3,1,31,1,30,20,10,60,,,,, 3,1,31,1,20,10,20,50,,,,, 3,1,31,1,30,20,30,80,,,,, 3,1,31,1,0,0,20,20,,,,, 3,1,31,1,30,30,30,90,,,,, 3,1,31,1,30,30,30,90,,,,, 3,1,31,1,30,30,30,90,,,,, 3,1,31,1,0,0,20,20,,,,, 3,1,31,1,20,10,10,40,,,,, 3,1,31,1,30,30,30,90,,,,, 3,1,31,1,20,20,30,70,,,,, 3,1,31,1,30,20,10,60,,,,, 3,1,31,1,10,10,30,50,,,,, Thank you.
You forgot to filter the Present column by True. You can do it this way. grades = grades[grades["Present"] == True] If the boolean is stored as a string then use the double quotation. grades = grades[grades["Present"] == "True"]
Transforming pandas dataframe, where column entries are column headers
My dataset has 12 columns, X1-X6 and Y1-Y6. The variables X and Y match to each other - the first record means: 80 parts of A, 10 parts of C, 2 parts of J and 8 parts of K (each row has 100 total). I would like to be able to transform my dataset into a dataset in which the entries in columns X1-X6 are now the headers. See before and after datasets below. My dataset (before): X1 X2 X3 X4 X5 X6 Y1 Y2 Y3 Y4 Y5 Y6 0 A C J K NaN NaN 80.0 10.0 2.0 8.0 NaN NaN 1 F N O NaN NaN NaN 2.0 25.0 73.0 NaN NaN NaN 2 A H J M NaN NaN 70.0 6.0 15.0 9.0 NaN NaN 3 B I K P NaN NaN 0.5 1.5 2.0 96.0 NaN NaN 4 A B F H O P 83.0 4.0 9.0 2.0 1.0 1.0 5 A B F G NaN NaN 1.0 16.0 9.0 74.0 NaN NaN 6 A B D F L NaN 95.0 2.0 1.0 1.0 1.0 NaN 7 B F H P NaN NaN 0.2 0.4 0.4 99.0 NaN NaN 8 A D F L NaN NaN 35.0 12.0 30.0 23.0 NaN NaN 9 A B F I O NaN 95.0 0.3 0.1 1.6 3.0 NaN 10 B E G NaN NaN NaN 10.0 31.0 59.0 NaN NaN NaN 11 A F G L NaN NaN 24.0 6.0 67.0 3.0 NaN NaN 12 A C I NaN NaN NaN 65.0 30.0 5.0 NaN NaN NaN 13 A F G L NaN NaN 55.0 6.0 4.0 35.0 NaN NaN 14 A F J K L NaN 22.0 3.0 12.0 0.8 62.2 NaN 15 B F I P NaN NaN 0.6 1.2 0.2 98.0 NaN NaN 16 A B F H O NaN 27.0 6.0 46.0 13.0 8.0 NaN The dataset I'd like to transform to: A B C D E F G H I J K L M \ 0 80.0 NaN 10.0 NaN NaN NaN NaN NaN NaN 2.0 8.0 NaN NaN 1 NaN NaN NaN NaN NaN 2.0 NaN NaN NaN NaN NaN NaN NaN 2 70.0 NaN NaN NaN NaN NaN NaN 6.0 NaN 15.0 NaN NaN 9.0 3 NaN 0.5 NaN NaN NaN NaN NaN NaN 1.5 NaN 2.0 NaN NaN 4 83.0 4.0 NaN NaN NaN 9.0 NaN 2.0 NaN NaN NaN NaN NaN 5 1.0 16.0 NaN NaN NaN 9.0 74.0 NaN NaN NaN NaN NaN NaN 6 95.0 2.0 NaN 1.0 NaN 1.0 NaN NaN NaN NaN NaN 1.0 NaN 7 NaN 0.2 NaN NaN NaN 0.4 NaN 0.4 NaN NaN NaN NaN NaN 8 35.0 NaN NaN 12.0 NaN 30.0 NaN NaN NaN NaN NaN 23.0 NaN 9 95.0 0.3 NaN NaN NaN 0.1 NaN NaN 1.6 NaN NaN NaN NaN 10 NaN 10.0 NaN NaN 31.0 NaN 59.0 NaN NaN NaN NaN NaN NaN 11 24.0 NaN NaN NaN NaN 6.0 67.0 NaN NaN NaN NaN 3.0 NaN 12 65.0 NaN 30.0 NaN NaN NaN NaN NaN 5.0 NaN NaN NaN NaN 13 55.0 NaN NaN NaN NaN 6.0 4.0 NaN NaN NaN NaN 35.0 NaN 14 22.0 NaN NaN NaN NaN 3.0 NaN NaN NaN 12.0 0.8 62.2 NaN 15 NaN 0.6 NaN NaN NaN 1.2 NaN NaN 0.2 NaN NaN NaN NaN 16 27.0 6.0 NaN NaN NaN 46.0 NaN 13.0 NaN NaN NaN NaN NaN N O P 0 NaN NaN NaN 1 25.0 73.0 NaN 2 NaN NaN NaN 3 NaN NaN 96.0 4 NaN 1.0 1.0 5 NaN NaN NaN 6 NaN NaN NaN 7 NaN NaN 99.0 8 NaN NaN NaN 9 NaN 3.0 NaN 10 NaN NaN NaN 11 NaN NaN NaN 12 NaN NaN NaN 13 NaN NaN NaN 14 NaN NaN NaN 15 NaN NaN 98.0 16 NaN 8.0 NaN
As you know that you want the Xi part to contain the column names for the new dataframe, while the Yi part would be the value, it is enough to change every line in a dict where Xi is the key and Yi the value. Then you use the list of that dictionnaries to feed the new dataframe: data = list(df.apply(lambda x: {x['X'+ str(i)]: x['Y'+str(i)] for i in range(1,7) if x['X'+str(i)]!= 'NaN'}, axis=1)) resul = pd.DataFrame(data) print(resul) gives: A B C D E F ... K L M N O P 0 80.0 NaN 10.0 NaN NaN NaN ... 8.0 NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN 2.0 ... NaN NaN NaN 25.0 73.0 NaN 2 70.0 NaN NaN NaN NaN NaN ... NaN NaN 9.0 NaN NaN NaN 3 NaN 0.5 NaN NaN NaN NaN ... 2.0 NaN NaN NaN NaN 96.0 4 83.0 4.0 NaN NaN NaN 9.0 ... NaN NaN NaN NaN 1.0 1.0 5 1.0 16.0 NaN NaN NaN 9.0 ... NaN NaN NaN NaN NaN NaN 6 95.0 2.0 NaN 1.0 NaN 1.0 ... NaN 1.0 NaN NaN NaN NaN 7 NaN 0.2 NaN NaN NaN 0.4 ... NaN NaN NaN NaN NaN 99.0 8 35.0 NaN NaN 12.0 NaN 30.0 ... NaN 23.0 NaN NaN NaN NaN 9 95.0 0.3 NaN NaN NaN 0.1 ... NaN NaN NaN NaN 3.0 NaN 10 NaN 10.0 NaN NaN 31.0 NaN ... NaN NaN NaN NaN NaN NaN 11 24.0 NaN NaN NaN NaN 6.0 ... NaN 3.0 NaN NaN NaN NaN 12 65.0 NaN 30.0 NaN NaN NaN ... NaN NaN NaN NaN NaN NaN 13 55.0 NaN NaN NaN NaN 6.0 ... NaN 35.0 NaN NaN NaN NaN 14 22.0 NaN NaN NaN NaN 3.0 ... 0.8 62.2 NaN NaN NaN NaN 15 NaN 0.6 NaN NaN NaN 1.2 ... NaN NaN NaN NaN NaN 98.0 16 27.0 6.0 NaN NaN NaN 46.0 ... NaN NaN NaN NaN 8.0 NaN [17 rows x 16 columns]
One way to handle this. Loop through each row, splitting the dataframe in half using iloc. Then build a new dictionary using zip, then create a resulting dataframe. df_dict = {x: list(zip(df.iloc[x,0:6], df.iloc[x,6:12])) for x in range(df.shape[0])} df1 = pd.DataFrame.from_dict(pd_dict, orient='index') df1.sort_index(1) A B C F H I J K M N O P nan 0 80.0 NaN 10.0 NaN NaN NaN 2.0 8.0 NaN NaN NaN NaN NaN 1 NaN NaN NaN 2.0 NaN NaN NaN NaN NaN 25.0 73.0 NaN NaN 2 70.0 NaN NaN NaN 6.0 NaN 15.0 NaN 9.0 NaN NaN NaN NaN 3 NaN 0.5 NaN NaN NaN 1.5 NaN 2.0 NaN NaN NaN 96. NaN 4 83.0 4.0 NaN 9.0 2.0 NaN NaN NaN NaN NaN 1.0 1.0 NaN
Sorting the columns of a pandas dataframe
Out[1015]: gp2 department MOBILE QA TA WEB MOBILE QA TA WEB minutes minutes minutes minutes growth growth growth growth period 2016-12-24 NaN NaN 140.0 400.0 NaN NaN 0.0 260.0 2016-12-25 NaN NaN NaN 80.0 NaN NaN NaN -320.0 2016-12-26 NaN NaN NaN 20.0 NaN NaN NaN -60.0 2016-12-27 NaN 45.0 NaN 180.0 NaN 25.0 NaN 135.0 2016-12-28 600.0 NaN NaN 15.0 420.0 NaN NaN -585.0 ... ... ... ... ... ... ... ... ... 2017-01-03 NaN NaN NaN 80.0 NaN NaN NaN -110.0 2017-01-04 20.0 NaN NaN NaN -60.0 NaN NaN NaN 2017-02-01 120.0 NaN NaN NaN 100.0 NaN NaN NaN 2017-02-02 45.0 NaN NaN NaN -75.0 NaN NaN NaN 2017-02-03 NaN 45.0 NaN 30.0 NaN 0.0 NaN -15.0 I need MOBILE.minutes and MOBILE.growth to be one after another. I tried this In [1019]:gp2.columns = gp2.columns.sort_values() In [1020]: gp2 Out[1020]: department MOBILE QA TA WEB growth minutes growth minutes growth minutes growth minutes period 2016-12-24 NaN NaN 140.0 400.0 NaN NaN 0.0 260.0 2016-12-25 NaN NaN NaN 80.0 NaN NaN NaN -320.0 2016-12-26 NaN NaN NaN 20.0 NaN NaN NaN -60.0 2016-12-27 NaN 45.0 NaN 180.0 NaN 25.0 NaN 135.0 2016-12-28 600.0 NaN NaN 15.0 420.0 NaN NaN -585.0 ... ... ... ... ... ... ... ... ... 2017-01-03 NaN NaN NaN 80.0 NaN NaN NaN -110.0 2017-01-04 20.0 NaN NaN NaN -60.0 NaN NaN NaN 2017-02-01 120.0 NaN NaN NaN 100.0 NaN NaN NaN 2017-02-02 45.0 NaN NaN NaN -75.0 NaN NaN NaN 2017-02-03 NaN 45.0 NaN 30.0 NaN 0.0 NaN -15.0 It sorted just the columns but didn't assign them proper values.
Just use df.sort_index: df = df.sort_index(level=[0, 1], axis=1) print(df) MOBILE QA TA WEB growth minutes growth minutes growth minutes growth minutes period 2016-12-24 NaN NaN NaN NaN 0.0 140.0 260.0 400.0 2016-12-25 NaN NaN NaN NaN NaN NaN -320.0 80.0 2016-12-26 NaN NaN NaN NaN NaN NaN -60.0 20.0 2016-12-27 NaN NaN 25.0 45.0 NaN NaN 135.0 180.0 2016-12-28 420.0 600.0 NaN NaN NaN NaN -585.0 15.0 2017-01-03 NaN NaN NaN NaN NaN NaN -110.0 80.0 2017-01-04 -60.0 20.0 NaN NaN NaN NaN NaN NaN 2017-02-01 100.0 120.0 NaN NaN NaN NaN NaN NaN 2017-02-02 -75.0 45.0 NaN NaN NaN NaN NaN NaN 2017-02-03 NaN NaN 0.0 45.0 NaN NaN -15.0 30.0
Merging columns and removing duplicates with Pandas
I need to merge similar columns and remove duplicates (entries with the same date). The data frame: Albumin C-reactive protein CRP Ferritin Haemoglobin Hb Iron Nancy Index Plasma Platelets Transferrin saturation % Transferrin saturations UCEIS (0 to 8) WCC White Cell Count test_date 0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 12.35 2016-04-17 23:00:00 1 NaN NaN NaN NaN 133.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 2016-04-17 23:00:00 2 NaN NaN NaN NaN NaN NaN NaN NaN NaN 406.0 NaN NaN NaN NaN NaN 2016-04-17 23:00:00 3 NaN NaN NaN NaN NaN NaN NaN NaN NaN 406.0 NaN NaN NaN NaN NaN 2016-04-17 23:00:00 4 NaN 32.2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 2016-04-17 23:00:00 5 36.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 2016-04-17 23:00:00 6 NaN NaN NaN 99.7 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 2016-04-17 23:00:00 7 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 25.0 NaN NaN NaN NaN 2016-04-17 23:00:00 12 36.0 NaN 32.2 99.7 NaN 133.0 NaN NaN NaN 406.0 NaN 25.0 NaN 12.35 NaN 2016-04-17 23:00:00 14 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 7.0 NaN NaN 2016-04-25 23:00:00 79 34.0 NaN 5.4 55.9 NaN 133.0 NaN NaN NaN 372.0 NaN 28.0 NaN 7.99 NaN 2016-06-12 23:00:00 I need to get: Albumin CRP Ferritin Hb Nancy Index Plasma Platelets Transferrin saturations UCEIS (0 to 8) WCC test_date 12 36.0 32.2 99.7 133.0 NaN NaN 406.0 25.0 NaN 12.35 2016-04-17 23:00:00 14 NaN NaN NaN NaN NaN NaN NaN NaN 7.0 NaN 2016-04-25 23:00:00 79 34.0 5.4 55.9 133.0 NaN NaN 372.0 28.0 NaN 7.99 2016-06-12 23:00:00 So, columns 'C-reactive protein' should be merged with 'CRP', 'Hemoglobin' with 'Hb', 'Transferrin saturation %' with 'Transferrin saturation'. I can easily remove duplicates with .drop_duplicates(), but the trick is remove not only row with the same date, but also to make sure, that the values in the same column are duplicated. For example, 'C-reactive protein' at row '4' has the same values as 'CRP' in row '12', in addition, they both have the same entry date. Given all that, I need to have only 'CRP' column with values 32.2 and the date '2016-04-17' (plus other unique columns). EDIT Some entries are really duplicates (absolutely identical, due to system glitches), for example (last three rows, on 2016-06-20, indices '803' and '122'). Is the solution below capable of removing such identical rows? P.S. Thanks for the amazing and general solution for duplicate, but not identical entries. Albumin C-reactive protein CRP Ferritin Haemoglobin Hb Iron Nancy Index Plasma Platelets Transferrin saturation % Transferrin saturations UCEIS (0 to 8) WCC White Cell Count setName test_date 735 39.0 NaN 0.4 52.0 NaN 144.0 NaN NaN NaN 197.0 NaN 25.0 NaN 4.88 NaN Bloods 2016-05-31 23:00:00 803 40.0 NaN 0.2 81.0 NaN 147.0 NaN NaN NaN 234.0 NaN 35.0 NaN 8.47 NaN Bloods 2016-06-20 23:00:00 347 NaN NaN NaN NaN NaN NaN NaN NaN 1.0 NaN NaN NaN NaN NaN NaN Research Bloods 2016-06-20 23:00:00 122 40.0 NaN 0.2 81.9 NaN 147.0 NaN NaN NaN 234.0 NaN 35.0 NaN 8.47 NaN Bloods 2016-06-20 23:00:00
I think you need groupby with rename columns by dict: d = {'C-reactive protein':'CRP', 'Hemoglobin':'Hb', 'Transferrin saturation %':'Transferrin saturations'} df = df.groupby('test_date').max().rename(columns=d).groupby(axis=1, level=0).max() print (df) Albumin CRP Ferritin Haemoglobin Hb Iron \ test_date 2016-04-17 23:00:00 36.0 32.2 99.7 133.0 133.0 NaN 2016-04-25 23:00:00 NaN NaN NaN NaN NaN NaN 2016-06-12 23:00:00 34.0 5.4 55.9 NaN 133.0 NaN Nancy Index Plasma Platelets Transferrin saturations \ test_date 2016-04-17 23:00:00 NaN NaN 406.0 25.0 2016-04-25 23:00:00 NaN NaN NaN NaN 2016-06-12 23:00:00 NaN NaN 372.0 28.0 UCEIS (0 to 8) WCC White Cell Count test_date 2016-04-17 23:00:00 NaN 12.35 12.35 2016-04-25 23:00:00 7.0 NaN NaN 2016-06-12 23:00:00 NaN 7.99 NaN More general solution is reshape by melt, remove duplicates and then create DataFrame back: d = {'C-reactive protein':'CRP', 'Hemoglobin':'Hb', 'Transferrin saturation %':'Transferrin saturations'} df = df.rename(columns=d).groupby(axis=1, level=0).max() df = pd.melt(df, id_vars='test_date').dropna(subset=['value']).drop_duplicates() df = df.groupby(['test_date','variable'])['value'] \ .apply(lambda x: pd.Series(x.values)) \ .unstack(1) \ .reset_index(level=1, drop=True) \ .reset_index() \ .rename_axis(None,axis=1) print (df) test_date Albumin CRP Ferritin Hb Platelets \ 0 2016-04-17 23:00:00 1000.0 32.2 99.7 1000.0 406.0 1 2016-04-17 23:00:00 36.0 NaN NaN 133.0 NaN 2 2016-04-25 23:00:00 NaN NaN NaN NaN NaN 3 2016-06-12 23:00:00 34.0 5.4 55.9 133.0 372.0 Transferrin saturations UCEIS (0 to 8) WCC White Cell Count 0 25.0 NaN 12.35 12.35 1 NaN NaN NaN NaN 2 NaN 7.0 NaN NaN 3 28.0 NaN 7.99 NaN
What #jezrael was saying is that if you had a situation where: Albumin C-reactive protein CRP test_date 0 NaN NaN 32 2016-04-17 23:00:00 1 NaN 8.0 NaN 2016-04-17 23:00:00 then his method would erase the 8.0 reading and keep only the 32 (this is because he does it in two steps (or 3?), in this line: df = df.groupby('test_date').max().rename(columns=d).groupby(axis=1, level=0).max() df = df.groupby('test_date').max() # selects max of each column # while collapsing 'test_date' which for my truncated example would give: Albumin C-reactive protein CRP test_date 0 NaN 8.0 32 2016-04-17 23:00:00 then rename .rename(columns=d) giving: Albumin CRP CRP test_date 0 NaN 8.0 32 2016-04-17 23:00:00 then .groupby(axis=1, level=0).max() to group along rows (instead of down columns) which gives: Albumin CRP test_date 0 NaN 32 2016-04-17 23:00:00 which is where you run the highest risk of losing data. Alternative I would split the original data into two frames first df1 = df[["C-reactive protein","Haemoglobin", ...]] df2 = df[["CRP", "Hb"]] # then rename df2 = df2.rename(columns={"CRP":"C-reactive protein", "Hb":"Haemoglobin", ...}) # use concat to stack them on one another df3 = pd.concat([df1, df2]) # i've run out of names df3 = df3.drop_duplicates() # perhaps also drop NAs? but this is only necessary if you have multiple non-duplicate entries for the same test on the same day.