Питон панды - построение многомерной сводной таблицы, чтобы отобразить количество Нанс и non-Нанс
У меня есть набор данных, основанный на различных метеостанциях для нескольких переменных (температура, давление и т. д.),
stationID | Time | Temperature | Pressure |...
----------+------+-------------+----------+
123 | 1 | 30 | 1010.5 |
123 | 2 | 31 | 1009.0 |
202 | 1 | 24 | NaN |
202 | 2 | 24.3 | NaN |
202 | 3 | NaN | 1000.3 |
...
И я хотел бы создать сводную таблицу, которая показывала бы количество NaNs и non-NaNs на метеостанцию, таких что:
stationID | nanStatus | Temperature | Pressure |...
----------+-----------+-------------+----------+
123 | NaN | 0 | 0 |
| nonNaN | 2 | 2 |
202 | NaN | 1 | 2 |
| nonNaN | 2 | 1 |
...
Ниже я покажу, что я сделал до сих пор, что работает (в громоздком виде) для температуры. Но как я могу получить то же самое для обеих переменных, как показано выше?
import pandas as pd
import bumpy as np
df = pd.DataFrame({'stationID':[123,123,202,202,202], 'Time':[1,2,1,2,3],'Temperature':[30,31,24,24.3,np.nan],'Pressure':[1010.5,1009.0,np.nan,np.nan,1000.3]})
dfnull = df.isnull()
dfnull['stationID'] = df['stationID']
dfnull['tempValue'] = df['Temperature']
dfnull.pivot_table(values=["tempValue"], index=["stationID","Temperature"], aggfunc=len,fill_value=0)
Вывод:
----------------------------------
tempValue
stationID | Temperature
123 | False 2
202 | False 2
| True 1
3 ответов:
Обновление: Спасибо @root:
In [16]: df.groupby('stationID')[['Temperature','Pressure']].agg([nans, notnans]).astype(int).stack(level=1) Out[16]: Temperature Pressure stationID 123 nans 0 0 notnans 2 2 202 nans 1 2 notnans 2 1Оригинальный ответ:
In [12]: %paste def nans(s): return s.isnull().sum() def notnans(s): return s.notnull().sum() ## -- End pasted text -- In [37]: df.groupby('stationID')[['Temperature','Pressure']].agg([nans, notnans]).astype(np.int8) Out[37]: Temperature Pressure nans notnans nans notnans stationID 123 0 2 0 2 202 1 2 2 1
Я признаю, что это не самое красивое решение, но оно работает. Сначала определите два временных столбца
TempNaNиPresNaN:df['TempNaN'] = df['Temperature'].apply(lambda x: 'NaN' if x!=x else 'NonNaN') df['PresNaN'] = df['Pressure'].apply(lambda x: 'NaN' if x!=x else 'NonNaN')Затем определите фрейм данных результатов с помощью Мультииндекса:
Results = pd.DataFrame(index=pd.MultiIndex.from_tuples(list(zip(*[sorted(list(df['stationID'].unique())*2),['NaN','NonNaN']*df['stationID'].nunique()])),names=['stationID','NaNStatus']))Храните свои вычисления в фрейме данных результатов:
Results['Temperature'] = df.groupby(['stationID','TempNaN'])['Temperature'].apply(lambda x: x.shape[0]) Results['Pressure'] = df.groupby(['stationID','PresNaN'])['Pressure'].apply(lambda x: x.shape[0])И заполните пустые значения нулем:
Results.fillna(value=0,inplace=True)Вы можете перебирать столбцы, если это проще. Например:
Results = pd.DataFrame(index=pd.MultiIndex.from_tuples(list(zip(*[sorted(list(df['stationID'].unique())*2),['NaN','NonNaN']*df['stationID'].nunique()])),names=['stationID','NaNStatus'])) for col in ['Temperature','Pressure']: df[col + 'NaN'] = df[col].apply(lambda x: 'NaN' if x!=x else 'NonNaN') Results[col] = df.groupby(['stationID',col + 'NaN'])[col].apply(lambda x: x.shape[0]) df.drop([col + 'NaN'],axis=1,inplace=True) Results.fillna(value=0,inplace=True)
d = {'stationID':[], 'nanStatus':[], 'Temperature':[], 'Pressure':[]} for station_id, data in df.groupby(['stationID']): temp_nans = data.isnull().Temperature.mean()*data.isnull().Temperature.count() pres_nans = data.isnull().Pressure.mean()*data.isnull().Pressure.count() d['stationID'].append(station_id) d['nanStatus'].append('NaN') d['Temperature'].append(temp_nans) d['Pressure'].append(pres_nans) d['stationID'].append(station_id) d['nanStatus'].append('nonNaN') d['Temperature'].append(data.isnull().Temperature.count() - temp_nans) d['Pressure'].append(data.isnull().Pressure.count() - pres_nans) df2 = pd.DataFrame.from_dict(d) print(df2)Результат таков:
Pressure Temperature nanStatus stationID 0 0.0 0.0 NaN 123 1 2.0 2.0 nonNaN 123 2 2.0 1.0 NaN 202 3 1.0 2.0 nonNaN 202
Comments