LITTLE BY LITTLE

[ch_03] Hands on Data Analysis With Pandas - Data Wrangling (reindex, pivoting, unstack, melt, clip, ffill, interpolate ..) 본문

4-2/Hands On Data Aanalysis With Pandas

[ch_03] Hands on Data Analysis With Pandas - Data Wrangling (reindex, pivoting, unstack, melt, clip, ffill, interpolate ..)

위나 2022. 10. 6. 19:21

ch_03.

  1. wide_vs_long
    1. wide가 일반적으로 많이 본 형태, describe로 한 눈에 볼때 편리, pandas의 matplotlib로 시각화
    2. long은 컬럼명이 data type과 value로 구성, describe하기에 적절치 않음, seaborn으로 시각화
  2. using_the_weather_api
  3. cleaning_data
    1. df.rename(str.supper(axis='columns').columns
    2. pd.to_datetime(df.col) 
    3. pd.tz_localize(), pd.tz_convert(), pd.tz_localize(None).to_period('M').to_timestamp().index 
    4. assign()으로 한번에 데이터 타입 변경 (df.col.astype('int' or ..))
    5. 범주형으로 변경 col = df.col.astpe('category')
    6. 카테고리 생성 pd.Categorical(['a','b','a','a','c','c'], categories=['A','B','C'],ordered=True)
    7. 범주형의 describe만 출력 df_with_categories.descrbie(include='category')
    8. df[df.datatype == 'TMAX'].sort_values(by='temp_C', ascending=False) 정렬
      1. 중복된 값의 경우 date 순서대로 정렬되지 않아서, date도 같이 정렬해주는 것이 좋다.
      2. df[df.datatype == 'TMAX'.sort_values(by=['temp_C','date'],ascending=[False,True]).head()
    9. df.sort_values(ignore_index=True) 할경우 정렬된 데이터프레임에 인덱스 새로 설정
    10. df.sort_index(axis=1).head().loc[:,'temp_C':'temp_F_whole'] loc 더 쉽게 가능
    11. .nlargest(), .nsmallest(), .sample()
    12. datetime은 슬라이싱할 때 처음과 끝 모두 포함(원래 끝 값은 제외)
    13. reset_index : 데이터를 잃고싶지 않을 때 사용
    14. date에서 day of week 컬럼 생성 => assign(day_of_week=lambda x: x.index.day_name())
      1. 인덱스가 달라지는 문제
      2. reindex가 필요 => reindex(df.index).head().assign(day_of_week=lambda x:x.index.day_name())
    15. reindex 옵션으로 Nan 채우기 - df.reindex(df.index,method='ffill')
    16. df.col.fillna(0) / df.col.fillna(method='ffill')
  4. reshaping_data
    1. long은 데이터들이 행(->)방향으로 나열, wide는 열 방향(아래)으로 나열
    2. pivoting : index, columns, values 세가지를 지정해서 자유롭게 데이터프레임을 만들어서 볼 수 있다.
    3. 특정 컬럼 순서로 보고싶을 때 pivoted_df['temp_F']['TMIN']
    4. 인덱스 여러개(멀티 인덱스) 설정 가능 df.set_index(['col1','col2'])
    5. 멀티 인덱스 해제 unstakced_df = multi_index_df.unstack()
      1. unstack에도 결측치 채우는 옵션 - df.unstack(fill_value=0)
    6. 결측치를 어떻게 채울지 보고싶을 때 unstack()을 많이 사용
    7. 데이터 추가 df.append([{..}]) <= 리스트 안에 딕셔너리 형태로
    8. melt : 컬럼을 녹여서 행으로 보냄
      1. wide_df.melt(id_vars='date', value_vars= ['col1','col2','col3'],value_name = 'value', var_name = 'col')
      2. pivoting 대신에 date를 인덱스로 설정하고,  stack() 한 후에 stacked_series.to_frame('values') 데이터프레임으로 바꾸면 melted됨
  5. handling_data_issues
    1. df.duplicated(keep=False)로 설정하면 중복된 여러개 값 중 첫번째는 중복에서 제외, 유지함
    2. col.combine_first(another_col) => 다른 객체로 결측치를 덮어 쓸 수 있다
    3. df.col.dropna(axis='column', how='all',subset=['col1','col2'],thresh=df.shape[0]*.75)
      1. how=all일경우 해당 열의 데이터가 전체다 null이어야만 삭제됨
      2. * thresh는 shape[0],즉 열의 개수 기준 데이터가 75%개 미만으로 입력되었을시 drop하는 옵션
    4. 매일 급격히 변하지않는 데이터의 경우, 바로 앞/뒤 값으로 결측치를 채우는 ffill, bfill이 적합한 방법
    5. NaN은 0으로, inf/-inf는 유한한 값으로 바꿔주는 np.nan_to_num(col)
    6. clip(lower bound, col) => clip도 np.nan_to_num과 같은 기능이지만, 최대/최대 threshold를 정할 수 있음 (ex. 음수가 될 수 없는 컬럼의 값의 lower bound를 0으로 정할 수 있다)
    7. fillna(x,rolling(7,min_periods=0).median() => 이동 중앙값으로 결측치를 채울 수 있다
      1. min_periods=0으로 설정하면 기간이 아무리 적어도 결괏값을 얻을 수 있음
    8. x.interpolate()  선형으로 결측치를 채우는 방법

3-1. wide VS long

wide

=> wide가 일반적으로 봐왔던 형태

import matplotlib.pyplot as plt
import pandas as pd

wide_df = pd.read_csv('data/wide_data.csv', parse_dates=['date'])
long_df = pd.read_csv(
    'data/long_data.csv', 
    usecols=['date', 'datatype', 'value'], 
    parse_dates=['date']
)[['date', 'datatype', 'value']] # sort columns

Wide(↓)는 describe로 한 눈에 볼 때 편리함

wide_df.describe(include='all', datetime_is_numeric=True)
wide_df.plot(
    x='date', y=['TMAX', 'TMIN', 'TOBS'], figsize=(15, 5), 
    title='Temperature in NYC in October 2018'
).set_ylabel('Temperature in Celsius')
plt.show()

Long

=> long은 컬럼명이 데이터 속에 들어가있고, data type, value로(↓) 나열
=> long은 describe하기에 적절치 않음
=> plot으로 그릴때에도 pandas가 아닌 seaborn 이용하는 것이 더 적합

import seaborn as sns

sns.set(rc={'figure.figsize': (15, 5)}, style='white')

ax = sns.lineplot(
    data=long_df, x='date', y='value', hue='datatype'
)
ax.set_ylabel('Temperature in Celsius')
ax.set_title('Temperature in NYC in October 2018')
plt.show()

long data와 seaborn으로 facet plots

sns.set(
    rc={'figure.figsize': (20, 10)}, style='white', font_scale=2
)

g = sns.FacetGrid(long_df, col='datatype', height=10)
g = g.map(plt.plot, 'date', 'value')
g.set_titles(size=25)
g.set_xticklabels(rotation=45)
plt.show()


3-2. Using the weather api

central_park 여기서 .,,


3-3. Cleaning Data

temperature.csv

import pandas as pd

df = pd.read_csv('/content/nyc_temperatures.csv')
df.head()

.rename()

df.rename(
    columns={
        'value': 'temp_C',
        'attributes': 'flags'
    }, inplace=True
)

rename + string operations

df.rename(str.upper, axis='columns').columns

pd.to_datetime()

df.loc[:,'date'] = pd.to_datetime(df.date)
df.dtypes

pd.tz_localize()

pd.tz_convert()

 

period monthly로 변경

eastern.tz_localize(None).to_period('M').index

.to_timestamp()

eastern.tz_localize(None).to_period('M').to_timestamp().index

.assign()으로 한번에 데이터 타입 변경

new_df = df.assign(
    date=pd.to_datetime(df.date),
    temp_F=(df.temp_C * 9/5) + 32
)

assign()은 주로 lambda식과 함께 씀, astype() 사용

df = df.assign(
    date=lambda x: pd.to_datetime(x.date),
    temp_C_whole=lambda x: x.temp_C.astype('int'),
    temp_F=lambda x: (x.temp_C * 9/5) + 32,
    temp_F_whole=lambda x: x.temp_F.astype('int')
)

카테고리 생성, astype('category')

df_with_categories = df.assign(
    station=df.station.astype('category'),
    datatype=df.datatype.astype('category')
)
df_with_categories.dtypes

카테고리 정렬

pd.Categorical(
    ['med', 'med', 'low', 'high'], 
    categories=['low', 'med', 'high'],
    ordered=True
)
df_with_categories.describe(include='category')
카테고리 describe

 

df['정렬대상 컬럼'].sort_values(by='정렬기준 컬럼')

df[df.datatype == 'TMAX'].sort_values(by='temp_C', ascending=False).head(10)

이렇게 할시, temp_C가 같은 값일 경우, date가 뒤죽박죽되어 출력, date도 정렬해주기

df[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True]).head(10)

sort_values(ignore_index=True) 정렬된 데이터프레임에 인덱스 새로 설정

df[df.datatype == 'TMAX'].sort_values(by=['temp_C', 'date'], ascending=[False, True], ignore_index=True).head(10)

 .nlargest

df[df.datatype == 'TAVG'].nlargest(n=10, columns='temp_C')

.nsmallest

df.nsmallest(n=5, columns=['temp_C', 'date'])
.sample()
df.sample(5, random_state=0).index

.sort_index() 인덱스도 정렬가능

df.sort_index(axis=1).head()

.sort_index()이용해서 loc 더 쉽게 할 수 있다

df.sort_index(axis=1).head().loc[:,'temp_C':'temp_F_whole']

* 같은 데이터이더라도, 인덱스가 다르면 not-equal 출력 (ex. sort_values한 df와 그냥 df는 다름)
=> 하지만 sort_values한 data frame도 sort_index하면 같아짐
2018년 출력

df.loc['2018']

2018년 4분기 출력

df.loc['2018-Q4']

datetime은 slicing할 때 inclusive of both endpoints

df['2018-10-11':'2018-10-12']

(*.reset_index()는 데이터를 잃고싶지 않을 때 (인덱스로 설정한 컬럼의 데이터) 자주 사용된다.)
index_col을 date로 설정해서 불러온 뒤, index인 date의 day를 호출해서 day_of_week 컬럼 추가로 생성
(컬럼 추가 생성으로 인해 인덱스가 달라지는 문제가 생김)

sp = pd.read_csv(
    '/content/sp500.csv', index_col='date', parse_dates=True
).drop(columns=['adj_close'])

sp.head(10).assign(
    day_of_week=lambda x: x.index.day_name()
)

근데 위의 경우 문제가 있다. reindex가 필요, 문제를 그래프로 그려서 확인

import matplotlib.pyplot as plt # we use this module for plotting
from matplotlib.ticker import StrMethodFormatter # for formatting the axis

# plot the closing price from Q4 2017 through Q2 2018
ax = portfolio['2017-Q4':'2018-Q2'].plot(
    y='close', figsize=(15, 5), legend=False,
    title='Bitcoin + S&P 500 value without accounting for different indices'
)

# formatting
ax.set_ylabel('price')
ax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))
for spine in ['top', 'right']:
    ax.spines[spine].set_visible(False)

# show the plot
plt.show()

align the index by using reindex() method

sp.reindex(bitcoin.index).head(10).assign(
    day_of_week=lambda x: x.index.day_name()
)

reindex 전

reindex 후
주말과 공휴일의 값이 NaN인 것을 forward-fill로 채워서 해결하자, reindex(index, method='ffill')

sp.reindex(bitcoin.index, method='ffill').head(10)\
    .assign(day_of_week=lambda x: x.index.day_name())

reindex(index).compare() method로 ffill이 잘 되었는지 확인

#To isolate the changes happening with the forward-filling, we can use the `compare()` method.

sp.reindex(bitcoin.index)\
    .compare(sp.reindex(bitcoin.index, method='ffill'))\
    .head(10).assign(day_of_week=lambda x: x.index.day_name())

self가 forward-fill 이전, other가 forward-fill 이후
=> 주말과 공휴일이 forward-filled로 채워진 값을 갖고있음을 알 수 있다.
추가로 0으로 결측치를 채워넣자

import numpy as np

sp_reindexed = sp.reindex(bitcoin.index).assign(
    volume=lambda x: x.volume.fillna(0), # put 0 when market is closed
    close=lambda x: x.close.fillna(method='ffill'), # carry this forward
    # take the closing price if these aren't available
    open=lambda x: np.where(x.open.isnull(), x.close, x.open),
    high=lambda x: np.where(x.high.isnull(), x.close, x.high),
    low=lambda x: np.where(x.low.isnull(), x.close, x.low)
)
sp_reindexed.head(10).assign(
    day_of_week=lambda x: x.index.day_name()
)

시각화해서 reindexing이 어떻게 market이 closed되었을 때의 asset value가 유지될 수 있게 해주었는지 확인

# every day's closing price = S&P 500 close adjusted for market closure + Bitcoin close (same for other metrics)
fixed_portfolio = sp_reindexed + bitcoin

# plot the reindexed portfolio's closing price from Q4 2017 through Q2 2018
ax = fixed_portfolio['2017-Q4':'2018-Q2'].plot(
    y='close', label='reindexed portfolio of S&P 500 + Bitcoin', figsize=(15, 5), linewidth=2, 
    title='Reindexed portfolio vs. portfolio with mismatched indices'
)

# add line for original portfolio for comparison
portfolio['2017-Q4':'2018-Q2'].plot(
    y='close', ax=ax, linestyle='--', label='portfolio of S&P 500 + Bitcoin w/o reindexing'
)

# formatting
ax.set_ylabel('price')
ax.yaxis.set_major_formatter(StrMethodFormatter('${x:,.0f}'))
for spine in ['top', 'right']:
    ax.spines[spine].set_visible(False)

# show the plot
plt.show()


 

3-4. Reshaping data

( wide ↓ / long → 에 따라 데이터 형식 바꿀 필요가 있음 )

  1. Pivoting
  2. unstack
  3. melt
long_df = pd.read_csv('data/long_data.csv',usecols = ['date','datatype','value']).rename(
    columns={'value':'temp_C'}).assign(date=lambda x: pd.to_datetime(x.date), 
                                       temp_F = lambda x: (x.temp_C * 9/5) + 32)
long_df.head()
wide_df = pd.read_csv('data/wide_data.csv')
wide_df.head()

[Out]

Long : 컬럼들이 행 방향으로(&rarr;) 나열 / Wide : 데이터들이 컬럼 열을 기준으로 아래로(&darr;) 나열

Transpose


Pivoting - 특정 컬럼을 인덱스로 쓸 때 (ex. 날짜 컬럼 기준으로 long format data 나열)

- index, columns, values 다 설정할 수 있다.

pivoted_df = long_df.pivot(
    index = 'date', columns='datatype', values = 'temp_C'
)
pivoted_df.head()

[Out]

values 여러개 설정

pivoted_df = long_df.pivot(index='date', columns='datatype',
                           values=['temp_C','temp_F'])
pivoted_df.head()

[Out]

특정 컬럼 순서로 보고싶을 때

pivoted_df['temp_F']['TMIN'].head()

[Out]


인덱스를 여러개 설정하고 싶을 때 set.index([리스트])

multi_index_df = long_df.set_index(['date','datatype'])
multi_index_df.head().index
multi_index_df.head()

[Out]

멀티 인덱스 해제 - unstack(<->pivoted)

unstacked_df = multi_index_df.unstack()
unstacked_df.head()

[Out]

unstack()은 결측치 채울 때 어떻게 채워야할지 보고자 하는데 도움을 준다
결측치를 임의로 만들기위해서 append로 데이터 추가해주기

extra_data = long_df.append([{
    'datatype' : 'TAVG',
    'date' : '2018-10-01',
    'temp_C' : 10,
    'temp_F' : 50
}]).set_index(['date','datatype']).sort_index()

extra_data['2018-10-01':'2018-10-02']

[Out]

extra_data.unstack().head()

[Out]

unstack해서 결측치가 어디에 있는지 한눈에 보임

unstack(fill_values) 옵션으로 결측치 채우기

extra_data.unstack(fill_value=-40).head()

[Out]


MELT : 컬럼을 녹여서 행으로 보냄

melted_df = wide_df.melt(id_vars = 'date', value_vars = ['TMAX', 'TMIN', 'TOBS'],
                         value_name = 'temp_C', var_name = 'measurement')
melted_df.head()

[Out] (오른쪽)

기존의 wide 포맷에서 우측처럼 melted 된 모습, long과 비슷해보이지만 다름!.!

Pivoting(인덱스 설정)하는 다른 방법은 1) stack()하고, 2) stack() 메소드로 melt 시키는 방법
1) stack() 하기

wide_df.set_index('date', inplace=True)
stacked_series = wide_df.stack() # 인덱스에 데이터타입 넣기
stacked_series.head()

[Out]
2) melt 시키기(컬럼을 녹여서 행으로) : to_frame으로 values 지정

stacked_df = stacked_series.to_frame('values')
stacked_df.head()

[Out] (왼쪽)

(왼) stack 후 melt시켜서 pivoting 시킨 결과 / (오)는 이전에 인덱스를 date로, 컬럼을 data type으로 설정하여 pivoting했던 결과
전 / 후 =&gt; pivoting 이후에 multi index로 바뀐 모습


3-5. Handling Data Issues

데이터 변수명 설명

df.describe()

[Out]

중복된 값, 결측치, inf 값 모두 있는 모습

df.info()

[Out]

Non-null인 데이터 개수가 다다름 => 결측치 처리 필요

컬럼별로 isna()를 데이터 프레임으로 만들어서 shape[0] 확인 => 결측치 총 개수는 765개

contain_nulls = df[
    df.SNOW.isna() | df.SNWD.isna() | df.TOBS.isna()
    | df.WESF.isna() | df.inclement_weather.isna()
]
contain_nulls.shape[0]
# 765
contain_nulls.head(10)

[Out]

결측치 있는 컬럼만 모은 데이터프레임

import numpy as np
df[df.inclement_weather == 'NaN'].shape[0] # doesn't work 
df[df.inclement_weather == np.nan].shape[0] # doesn't work

isna()써야 shape 확인 가능

df[df.inclement_weather.isna()].shape[0]

isin()

df[df.SNWD.isin([-np.inf])].shape[0]

na는(np.inf ~ -np.inf) 사이 값이 아니라서, 이런식으로 찾을경우 없다고 나옴

def get_inf_count(df):
  return {
      col: df[
          df[col].isin([np.inf, -np.inf])
      ].shape[0] for col in df.columns
  }
get_inf_count(df)

[Out]

{'date': 0,
 'station': 0,
 'PRCP': 0,
 'SNOW': 0,
 'SNWD': 577,
 'TMAX': 0,
 'TMIN': 0,
 'TOBS': 0,
 'WESF': 0,
 'inclement_weather': 0}

inf

snow depth 컬럼에 inf 값 존재 여부 확인

pd.DataFrame({
    'np.inf Snow Depth': df[df.SNWD == np.inf].SNOW.describe(),
    '-np.inf Snow Depth': df[df.SNWD == -np.inf].SNOW.describe()
}).T

[Out]


중복

범주형 변수만 describe로 개수 unique(=null이 아닌 값) 개수 확인

df.describe(include='object')

[Out]

duplicated()로 중복 값이 있는 row 찾기

df[df.duplicated()].shape[0]

df[df.duplicated(keep=False)].shape[0]

df[df.duplicated()].head()

=> 디폴트인 keep=True로 하면 중복된 값의 첫번째는 중복으로 치지 않음 => 284
=> keep=False로해서 첫번째 값도 중복으로 처리 => 284
=> 특정 컬럼의 중복값만 확인 가능
=> Let's look at a few duplicates. Just in the few values we see here, we know that the top 4 are actually in the data 6 times because by default we aren't seeing their first occurrence: ?????

# 1. 날짜 컬럼 datetime으로 형식 변경
df.date = pd.to_datetime(df.date)

# 2. save this information for later
station_qm_wesf = df[df.station == '?'].drop_duplicates('date').set_index('date').WESF

# 3. 값이 ?인 부분을 아래로 오게 정렬(내림차순)
df.sort_values('station', ascending=False, inplace=True)

# 4. keep=True로 설정해서 중복 값의 첫번째만 유지
# 값이 있을 시 valid한 station이 되도록
df_deduped = df.drop_duplicates('date')

# 5. 중복값 처리하기 이전 원본 컬럼 삭제
df_deduped = df_deduped.drop(columns='station').set_index('date').sort_index()

# 6. take valid station's WESF and fall back on station ? if it is null
df_deduped = df_deduped.assign(
    WESF=lambda x: x.WESF.combine_first(station_qm_wesf)
)

df_deduped.shape

=> 마지막에 combine_first() 메소드로 다른 객체로 결측치 덮어씀
=> 즉, valid한 WESF값을 갖고있는 데이터의 station 값을 결측치가 있는 데이터에 덮어 쓰는 것

combine_first() 예시


측치

df_deduped = df[df.duplicated()]

df_deduped.dropna().shape

df_deduped.dropna(how='all').shape # 디폴트는 how='any'

df_deduped.dropna(
    how='all', subset=['inclement_weather', 'SNOW', 'SNWD']).shape
    
df_deduped.dropna(
    axis='columns',
    thresh = df_deduped.shape[0] * .75 # 열의 75%라는 의미
).columns

=> dropna에서 how='all'으로 지정할경우, 값이 "전부 다" null인 경우에만 삭제하기 때문에, 아무것도 삭제되지 않음, shape이 원본과 같이 (324,8)
=> dropna에 subset=리스트로 특정 컬럼의 결측치만 drop
=> threshold를 정해서 컬럼을 기준으로 값이 n개 미만 입력되었을시 컬럼을 drop => drop thresh = df_deduped.shape[0] * .75 => 데이터의 shape 에서 데이터 수를 가져온 후 75% 값보다 적게 있으면 drop!
[Out]

Index(['date', 'station', 'PRCP', 'SNOW', 'SNWD', 'TMAX', 'TMIN'], dtype='object')

loc으로 특정 컬럼 선택해서 그 컬럼의 null을 fillna로 채우기

df_deduped.loc[:, 'WESF'].fillna(0, inplace=True)
df_deduped.head()

Unreasonable values

필수는 아니지만 경우에 따라 필요한 작업
temperature 데이터에서
1. TMAX (=temperature of the Sun) 에는 measured_value가 없어야하므로, 5505 => NaN으로 대체
2. TMIN 은 -40°C를 placeholder로 사용하고 있지만, NYC에서 가장 추운 날의 기온은 -26.1°C이기 때문에 -40°C=>NaN

df_deduped = df_deduped.assign(
    TMAX = lambda x: x.TMAX.replace(5505, np.nan),
    TMIN = lambda x: x.TMIN.replace(-40, np.nan)
)
df_deduped.assign(
    TMAX = lambda x: x.TMAX.fillna(method='ffill'),
    TMIN = lambda x: x.TMIN.fillna(method='ffill')
).head()

Unreasonable values => NaN으로 바꾼 뒤, 기온은 매일 급격히 변하지는 않기 때문에, 결측치를 바로 앞/뒤 기온으로 채워넣어주는 방법도 적합 (ffill/bfill)

df_deduped.assign(
    TMAX=lambda x: x.TMAX.fillna(method='ffill'),
    TMIN=lambda x: x.TMIN.fillna(method='ffill')
).head()

NaN와 inf 값을 np.nan_to_num() 함수로 처리

- NaN => 0
- inf/-inf는 => 아주 큰 유한한 값이 됨

df_deduped.assign(
    SNWD=lambda x: np.nan_to_num(x.SNWD)
).head()

[Out]

Clip() 메소드도 np.nan_to_num()과 같은 기능, 차이점은 특정 최소/최대 threshold를 정할 수 있다.
=> SNWD 컬럼은 음수가 될 수 없기 때문에, clip()으로 lower bound를 0으로 정하자

df_deduped.assign(
    SNWD=lambda x: x.SNWD.clip(0, x.SNOW)
).head()

[Out]

TMAX와 TMIN의 결측치를 median으로, TOBS의 결측치를 TMAX와 TMIN의 평균으로 정해주자
(assign + lambda 함수사용)

df_deduped.assign(
    TMAX=lambda x: x.TMAX.fillna(x.TMAX.median()),
    TMIN=lambda x: x.TMIN.fillna(x.TMIN.median()),
    # average of TMAX and TMIN
    TOBS=lambda x: x.TOBS.fillna((x.TMAX + x.TMIN) / 2)
).head()

[Out]

apply()를 이용해서 컬럼 사이 같은 연산을 적용
rolling 7-day median 이동 중앙값 으로 결측치를 채워넣자. => ch4.에서 rolling calculations 다룸

df_deduped.apply(
    # rolling calculations will be covered in chapter 4, this is a rolling 7-day median
    # we set min_periods (# of periods required for calculation) to 0 so we always get a result 
    lambda x: x.fillna(x.rolling(7, min_periods=0).median())
).head(10)

마지막은 interpolate() 으로 결측치 채우는 방법
디폴트는 linear
1월 9일 데이터가 없기 때문에, 8일과 10일의 평균값으로 (=interpolate) 결측치 채우기

df_deduped\
    .reindex(pd.date_range('2018-01-01', '2018-12-31', freq='D'))\
    .apply(lambda x: x.interpolate())\
    .head(10)

 

Comments