5. Pandas Advanced
5. Pandas Advanced¶
Previous: Pandas Data Manipulation | Next: Data Preprocessing
Overview¶
This covers advanced Pandas features including pivot tables, multi-index, time series data processing, and performance optimization techniques.
1. MultiIndex (MultiIndex)¶
1.1 ๋ฉํฐ์ธ๋ฑ์ค ์์ฑ¶
import pandas as pd
import numpy as np
# ํํ ๋ฆฌ์คํธ๋ก ์์ฑ
arrays = [
['A', 'A', 'B', 'B'],
[1, 2, 1, 2]
]
tuples = list(zip(*arrays))
index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
s = pd.Series([10, 20, 30, 40], index=index)
print(s)
# first second
# A 1 10
# 2 20
# B 1 30
# 2 40
# from_arrays
index = pd.MultiIndex.from_arrays(arrays, names=['first', 'second'])
# from_product (์นด๋ฅดํ
์์ ๊ณฑ)
index = pd.MultiIndex.from_product(
[['A', 'B'], [1, 2, 3]],
names=['letter', 'number']
)
print(index)
# DataFrame์ ์ ์ฉ
df = pd.DataFrame({
'value': [10, 20, 30, 40, 50, 60]
}, index=index)
print(df)
1.2 ๋ฉํฐ์ธ๋ฑ์ค DataFrame¶
# ์ด์๋ ๋ฉํฐ์ธ๋ฑ์ค
col_index = pd.MultiIndex.from_product(
[['2023', '2024'], ['Q1', 'Q2']],
names=['year', 'quarter']
)
row_index = pd.MultiIndex.from_product(
[['Sales', 'IT'], ['Seoul', 'Busan']],
names=['dept', 'city']
)
data = np.random.randint(100, 1000, (4, 4))
df = pd.DataFrame(data, index=row_index, columns=col_index)
print(df)
1.3 ๋ฉํฐ์ธ๋ฑ์ค ์ ํ¶
df = pd.DataFrame({
'year': [2022, 2022, 2023, 2023, 2022, 2023],
'quarter': ['Q1', 'Q2', 'Q1', 'Q2', 'Q1', 'Q1'],
'department': ['Sales', 'Sales', 'IT', 'IT', 'IT', 'Sales'],
'revenue': [100, 150, 200, 250, 180, 160]
})
df = df.set_index(['year', 'quarter', 'department'])
print(df)
# ๋จ์ผ ๋ ๋ฒจ ์ ํ
print(df.loc[2022])
print(df.loc[(2022, 'Q1')])
print(df.loc[(2022, 'Q1', 'Sales')])
# xs ๋ฉ์๋ (ํฌ๋ก์ค ์น์
)
print(df.xs('Q1', level='quarter'))
print(df.xs('Sales', level='department'))
print(df.xs((2022, 'Sales'), level=['year', 'department']))
# ์ฌ๋ผ์ด์ฑ
print(df.loc[2022:2023])
print(df.loc[(2022, 'Q1'):(2023, 'Q1')])
1.4 ๋ฉํฐ์ธ๋ฑ์ค ์กฐ์¶
df = pd.DataFrame({
'year': [2022, 2022, 2023, 2023],
'quarter': ['Q1', 'Q2', 'Q1', 'Q2'],
'revenue': [100, 150, 200, 250]
}).set_index(['year', 'quarter'])
# ๋ ๋ฒจ ๊ตํ
print(df.swaplevel())
# ๋ ๋ฒจ ์ ๋ ฌ
df_unsorted = df.iloc[[2, 0, 3, 1]]
print(df_unsorted.sort_index())
print(df_unsorted.sort_index(level=1))
# ์ธ๋ฑ์ค ๋ฆฌ์
print(df.reset_index())
print(df.reset_index(level='quarter'))
# ๋ ๋ฒจ ์ด๋ฆ ๋ณ๊ฒฝ
df.index = df.index.set_names(['์ฐ๋', '๋ถ๊ธฐ'])
print(df)
# ๋ ๋ฒจ ๊ฐ ๋ณ๊ฒฝ
df.index = df.index.set_levels([['2022๋
', '2023๋
'], ['1๋ถ๊ธฐ', '2๋ถ๊ธฐ']])
1.5 ๋ฉํฐ์ธ๋ฑ์ค ์ง๊ณ¶
df = pd.DataFrame({
'year': [2022, 2022, 2022, 2023, 2023, 2023],
'quarter': ['Q1', 'Q1', 'Q2', 'Q1', 'Q1', 'Q2'],
'department': ['Sales', 'IT', 'Sales', 'Sales', 'IT', 'Sales'],
'revenue': [100, 150, 120, 200, 180, 220]
}).set_index(['year', 'quarter', 'department'])
# ๋ ๋ฒจ๋ณ ํฉ๊ณ
print(df.groupby(level='year').sum())
print(df.groupby(level=['year', 'quarter']).sum())
# unstack์ผ๋ก ํผ๋ฒ
print(df.unstack(level='department'))
print(df.unstack(level=['quarter', 'department']))
# stack์ผ๋ก ์ญํผ๋ฒ
df_wide = df.unstack(level='department')
print(df_wide.stack())
2. Time Series Data¶
2.1 ๋ ์ง/์๊ฐ ์์ฑ¶
# Timestamp
ts = pd.Timestamp('2023-01-15')
ts = pd.Timestamp('2023-01-15 10:30:00')
ts = pd.Timestamp(year=2023, month=1, day=15, hour=10)
# to_datetime
dates = pd.to_datetime(['2023-01-01', '2023-02-01', '2023-03-01'])
dates = pd.to_datetime(['01/15/2023', '02/15/2023'], format='%m/%d/%Y')
# date_range
dates = pd.date_range('2023-01-01', periods=10, freq='D')
dates = pd.date_range('2023-01-01', '2023-12-31', freq='M') # ์๋ง
dates = pd.date_range('2023-01-01', '2023-12-31', freq='MS') # ์์ด
dates = pd.date_range('2023-01-01', periods=5, freq='W-MON') # ๋งค์ฃผ ์์์ผ
# ์ฃผ์ freq ์ต์
# 'D': ์ผ, 'W': ์ฃผ, 'M': ์๋ง, 'MS': ์์ด
# 'Q': ๋ถ๊ธฐ๋ง, 'QS': ๋ถ๊ธฐ์ด, 'Y': ์ฐ๋ง, 'YS': ์ฐ์ด
# 'H': ์๊ฐ, 'T' or 'min': ๋ถ, 'S': ์ด
# 'B': ์์
์ผ, 'BM': ์์
์ผ ์๋ง
# period_range (๊ธฐ๊ฐ)
periods = pd.period_range('2023-01', periods=12, freq='M')
print(periods)
2.2 ์๊ณ์ด ์ธ๋ฑ์ฑ¶
# DatetimeIndex๋ฅผ ๊ฐ์ง Series
dates = pd.date_range('2023-01-01', periods=365, freq='D')
ts = pd.Series(np.random.randn(365), index=dates)
# ๋ฌธ์์ด๋ก ์ ํ
print(ts['2023-03-15'])
print(ts['2023-03']) # 3์ ์ ์ฒด
print(ts['2023']) # 2023๋
์ ์ฒด
# ๋ฒ์ ์ ํ
print(ts['2023-03-01':'2023-03-10'])
print(ts['2023-03':'2023-06'])
# loc ์ฌ์ฉ
print(ts.loc['2023-03-15'])
print(ts.loc['2023-03'])
2.3 ๋ ์ง/์๊ฐ ์์ฑ¶
df = pd.DataFrame({
'date': pd.date_range('2023-01-01', periods=10, freq='D')
})
# dt ์ ๊ทผ์
df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month
df['day'] = df['date'].dt.day
df['dayofweek'] = df['date'].dt.dayofweek # 0=์์์ผ
df['day_name'] = df['date'].dt.day_name()
df['month_name'] = df['date'].dt.month_name()
df['quarter'] = df['date'].dt.quarter
df['is_month_end'] = df['date'].dt.is_month_end
df['is_month_start'] = df['date'].dt.is_month_start
print(df)
2.4 ์๊ฐ ์ฐ์ฐ¶
# Timedelta
td = pd.Timedelta('1 days')
td = pd.Timedelta(days=1, hours=2, minutes=30)
# ๋ ์ง ์ฐ์ฐ
dates = pd.date_range('2023-01-01', periods=5, freq='D')
print(dates + pd.Timedelta('1 days'))
print(dates + pd.DateOffset(months=1))
# ๋ ์ง ์ฐจ์ด
df = pd.DataFrame({
'start': pd.to_datetime(['2023-01-01', '2023-02-15']),
'end': pd.to_datetime(['2023-01-10', '2023-03-20'])
})
df['duration'] = df['end'] - df['start']
df['days'] = df['duration'].dt.days
# DateOffset
from pandas.tseries.offsets import MonthEnd, BDay
date = pd.Timestamp('2023-01-15')
print(date + MonthEnd()) # ์๋ง
print(date + BDay(5)) # 5 ์์
์ผ ํ
2.5 ๋ฆฌ์ํ๋ง¶
dates = pd.date_range('2023-01-01', periods=100, freq='D')
ts = pd.Series(np.random.randn(100), index=dates)
# ๋ค์ด์ํ๋ง (๋ ํฐ ๊ฐ๊ฒฉ์ผ๋ก)
print(ts.resample('W').mean()) # ์ฃผ๊ฐ ํ๊ท
print(ts.resample('M').sum()) # ์๊ฐ ํฉ๊ณ
print(ts.resample('M').agg(['mean', 'std', 'min', 'max']))
# ์
์ํ๋ง (๋ ์์ ๊ฐ๊ฒฉ์ผ๋ก)
monthly = pd.Series([100, 110, 120],
index=pd.date_range('2023-01-01', periods=3, freq='M'))
print(monthly.resample('D').ffill()) # ์์ ๊ฐ์ผ๋ก ์ฑ์
print(monthly.resample('D').bfill()) # ๋ค์ ๊ฐ์ผ๋ก ์ฑ์
print(monthly.resample('D').interpolate()) # ๋ณด๊ฐ
# OHLC ์ง๊ณ
print(ts.resample('W').ohlc()) # Open, High, Low, Close
2.6 ์ด๋ ์๋์ฐ¶
dates = pd.date_range('2023-01-01', periods=30, freq='D')
ts = pd.Series(np.random.randn(30).cumsum(), index=dates)
# ์ด๋ ํ๊ท
print(ts.rolling(window=7).mean())
# ๋ค์ํ ์ง๊ณ
print(ts.rolling(window=7).std())
print(ts.rolling(window=7).min())
print(ts.rolling(window=7).max())
print(ts.rolling(window=7).sum())
# ์ค์ฌ ์ด๋ ํ๊ท
print(ts.rolling(window=7, center=True).mean())
# ์ง์ ๊ฐ์ค ์ด๋ ํ๊ท (EWMA)
print(ts.ewm(span=7).mean())
print(ts.ewm(alpha=0.3).mean())
# ํ์ฅ ์๋์ฐ (์ฒ์๋ถํฐ ํ์ฌ๊น์ง)
print(ts.expanding().mean()) # ๋์ ํ๊ท
print(ts.expanding().sum()) # ๋์ ํฉ
2.7 ์๊ฐ๋ ์ฒ๋ฆฌ¶
# ์๊ฐ๋ ์ง์
ts = pd.Timestamp('2023-01-15 10:00', tz='Asia/Seoul')
print(ts)
dates = pd.date_range('2023-01-01', periods=5, freq='D', tz='UTC')
print(dates)
# ์๊ฐ๋ ๋ณํ
ts_utc = pd.Timestamp('2023-01-15 10:00', tz='UTC')
ts_seoul = ts_utc.tz_convert('Asia/Seoul')
print(ts_seoul)
# ์๊ฐ๋ ์ง์ญํ
ts_naive = pd.Timestamp('2023-01-15 10:00')
ts_localized = ts_naive.tz_localize('Asia/Seoul')
print(ts_localized)
# Series/DataFrame
s = pd.Series([1, 2, 3], index=pd.date_range('2023-01-01', periods=3, freq='D'))
s_utc = s.tz_localize('UTC')
s_seoul = s_utc.tz_convert('Asia/Seoul')
3. Categorical Data¶
3.1 Categorical ํ์ ¶
# Categorical ์์ฑ
cat = pd.Categorical(['a', 'b', 'c', 'a', 'b'])
print(cat)
print(cat.categories)
print(cat.codes)
# ์์ ์ง์
cat = pd.Categorical(['low', 'medium', 'high', 'low'],
categories=['low', 'medium', 'high'],
ordered=True)
print(cat)
print(cat.min()) # low
print(cat.max()) # high
# DataFrame์์ ์ฌ์ฉ
df = pd.DataFrame({
'grade': pd.Categorical(['A', 'B', 'A', 'C', 'B'],
categories=['C', 'B', 'A'],
ordered=True)
})
print(df.sort_values('grade'))
3.2 ํ์ ๋ณํ¶
df = pd.DataFrame({
'category': ['apple', 'banana', 'apple', 'cherry', 'banana']
})
# category ํ์
์ผ๋ก ๋ณํ
df['category'] = df['category'].astype('category')
print(df['category'].dtype)
print(df['category'].cat.categories)
# ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ ํ์ธ
df_str = pd.DataFrame({'col': ['A'] * 1000000})
df_cat = pd.DataFrame({'col': pd.Categorical(['A'] * 1000000)})
print(f"๋ฌธ์์ด: {df_str.memory_usage(deep=True).sum():,} bytes")
print(f"์นดํ
๊ณ ๋ฆฌ: {df_cat.memory_usage(deep=True).sum():,} bytes")
3.3 ๋ฒ์ฃผ ์กฐ์¶
s = pd.Series(['a', 'b', 'c', 'a', 'b']).astype('category')
# ์นดํ
๊ณ ๋ฆฌ ์ถ๊ฐ
s = s.cat.add_categories(['d', 'e'])
print(s.cat.categories)
# ์นดํ
๊ณ ๋ฆฌ ์ ๊ฑฐ
s = s.cat.remove_categories(['e'])
# ์นดํ
๊ณ ๋ฆฌ ์ด๋ฆ ๋ณ๊ฒฝ
s = s.cat.rename_categories({'a': 'A', 'b': 'B', 'c': 'C'})
print(s)
# Usage๋์ง ์๋ ์นดํ
๊ณ ๋ฆฌ ์ ๊ฑฐ
s = s.cat.remove_unused_categories()
# ์นดํ
๊ณ ๋ฆฌ ์ฌ์ ๋ ฌ
s = s.cat.reorder_categories(['C', 'B', 'A'])
4. Advanced String Operations¶
4.1 ์ ๊ท ํํ์¶
df = pd.DataFrame({
'text': ['apple 123', 'banana 456', 'cherry 789', 'date'],
'email': ['test@example.com', 'user@domain.org', 'invalid', 'admin@site.net']
})
# ํจํด ๋งค์นญ
print(df['text'].str.contains(r'\d+', regex=True))
# ํจํด ์ถ์ถ
print(df['text'].str.extract(r'(\w+)\s(\d+)'))
# ๋ชจ๋ ๋งค์น ์ถ์ถ
print(df['text'].str.findall(r'\d'))
# ์ด๋ฉ์ผ ๋๋ฉ์ธ ์ถ์ถ
print(df['email'].str.extract(r'@(.+)$'))
# ๊ต์ฒด
print(df['text'].str.replace(r'\d+', 'NUM', regex=True))
4.2 ๋ฌธ์์ด ๋ถ๋ฆฌ์ ๊ฒฐํฉ¶
df = pd.DataFrame({
'full_name': ['John Smith', 'Jane Doe', 'Bob Johnson']
})
# ๋ถ๋ฆฌ
names = df['full_name'].str.split(' ', expand=True)
names.columns = ['first', 'last']
print(names)
# ๊ฒฐํฉ
df['formatted'] = df['full_name'].str.replace(' ', ', ')
# ๋ฌธ์์ด ๊ฒฐํฉ (Series)
s = pd.Series(['a', 'b', 'c'])
print(s.str.cat(sep='-')) # 'a-b-c'
# ๋ Series ๊ฒฐํฉ
s1 = pd.Series(['a', 'b', 'c'])
s2 = pd.Series(['1', '2', '3'])
print(s1.str.cat(s2, sep='-')) # ['a-1', 'b-2', 'c-3']
4.3 ๋ฌธ์์ด ํฌ๋งทํ ¶
df = pd.DataFrame({
'name': ['Alice', 'Bob'],
'score': [95.5, 87.3]
})
# ํฌ๋งทํ
df['formatted'] = df['name'] + ': ' + df['score'].astype(str)
df['formatted2'] = df.apply(lambda x: f"{x['name']}: {x['score']:.1f}", axis=1)
print(df)
# ํจ๋ฉ
s = pd.Series(['1', '22', '333'])
print(s.str.pad(5, side='left', fillchar='0')) # ['00001', '00022', '00333']
print(s.str.zfill(5)) # ['00001', '00022', '00333']
print(s.str.center(7, '*')) # ['***1***', '**22***', '*333**']
5. Performance Optimization¶
5.1 ๋ฐ์ดํฐ ํ์ ์ต์ ํ¶
def reduce_mem_usage(df):
"""DataFrame ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ต์ ํ"""
start_mem = df.memory_usage().sum() / 1024**2
for col in df.columns:
col_type = df[col].dtype
if col_type != object:
c_min = df[col].min()
c_max = df[col].max()
if str(col_type)[:3] == 'int':
if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
df[col] = df[col].astype(np.int8)
elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
df[col] = df[col].astype(np.int16)
elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
df[col] = df[col].astype(np.int32)
else:
if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
df[col] = df[col].astype(np.float16)
elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
df[col] = df[col].astype(np.float32)
end_mem = df.memory_usage().sum() / 1024**2
print(f'๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋: {start_mem:.2f} MB -> {end_mem:.2f} MB ({100 * (start_mem - end_mem) / start_mem:.1f}% ๊ฐ์)')
return df
5.2 ๋ฒกํฐํ ์ฐ์ฐ¶
import time
df = pd.DataFrame({
'A': np.random.randn(100000),
'B': np.random.randn(100000)
})
# ๋์ ์: iterrows
start = time.time()
result = []
for idx, row in df.iterrows():
result.append(row['A'] + row['B'])
print(f"iterrows: {time.time() - start:.4f}์ด")
# ์ข์ ์: ๋ฒกํฐํ
start = time.time()
result = df['A'] + df['B']
print(f"๋ฒกํฐํ: {time.time() - start:.4f}์ด")
# apply vs ๋ฒกํฐํ
start = time.time()
result = df.apply(lambda x: x['A'] + x['B'], axis=1)
print(f"apply: {time.time() - start:.4f}์ด")
5.3 eval๊ณผ query¶
df = pd.DataFrame({
'A': np.random.randn(100000),
'B': np.random.randn(100000),
'C': np.random.randn(100000)
})
# eval ์ฌ์ฉ (๋ณต์กํ ์์)
df['D'] = pd.eval('df.A + df.B * df.C')
# ๋ ๋ณต์กํ ๊ณ์ฐ
result = df.eval('(A + B) / (C + 1)')
# query์ ํจ๊ป
result = df.query('A > 0 and B < 0')
# ์ง์ญ ๋ณ์ ์ฌ์ฉ
threshold = 0.5
result = df.query('A > @threshold')
5.4 ๋์ฉ๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ¶
# ์ฒญํฌ ๋จ์๋ก ์ฒ๋ฆฌ
def process_large_file(filename, chunksize=10000):
results = []
for chunk in pd.read_csv(filename, chunksize=chunksize):
# ๊ฐ ์ฒญํฌ ์ฒ๋ฆฌ
processed = chunk[chunk['value'] > 0].groupby('category')['value'].sum()
results.append(processed)
return pd.concat(results).groupby(level=0).sum()
# ํน์ ์ด๋ง ์ฝ๊ธฐ
df = pd.read_csv('large_file.csv', usecols=['col1', 'col2', 'col3'])
# ๋ฐ์ดํฐ ํ์
์ง์ ํ์ฌ ์ฝ๊ธฐ
dtypes = {'col1': 'int32', 'col2': 'float32', 'col3': 'category'}
df = pd.read_csv('large_file.csv', dtype=dtypes)
6. Pipelines¶
6.1 pipe ๋ฉ์๋¶
def remove_outliers(df, column, n_std=3):
"""์ด์์น ์ ๊ฑฐ"""
mean = df[column].mean()
std = df[column].std()
return df[(df[column] - mean).abs() <= n_std * std]
def add_features(df):
"""ํน์ฑ ์ถ๊ฐ"""
df = df.copy()
df['log_value'] = np.log1p(df['value'])
df['squared'] = df['value'] ** 2
return df
def normalize(df, columns):
"""์ ๊ทํ"""
df = df.copy()
for col in columns:
df[col] = (df[col] - df[col].min()) / (df[col].max() - df[col].min())
return df
# ํ์ดํ๋ผ์ธ ์คํ
df = pd.DataFrame({
'value': np.random.randn(1000) * 10 + 50,
'category': np.random.choice(['A', 'B', 'C'], 1000)
})
result = (df
.pipe(remove_outliers, 'value')
.pipe(add_features)
.pipe(normalize, ['value', 'log_value'])
)
print(result.head())
6.2 ๋ฉ์๋ ์ฒด์ด๋¶
df = pd.DataFrame({
'name': [' Alice ', 'BOB', 'charlie', None],
'age': [25, 30, 35, 28],
'salary': [50000, 60000, 70000, 55000]
})
result = (df
.dropna()
.assign(name=lambda x: x['name'].str.strip().str.title())
.query('age >= 25')
.sort_values('salary', ascending=False)
.reset_index(drop=True)
)
print(result)
Practice Problems¶
Problem 1: ๋ฉํฐ์ธ๋ฑ์ค ํ์ฉ¶
์ฐ๋๋ณ, ๋ถ๊ธฐ๋ณ ๋งค์ถ ๋ฐ์ดํฐ์์ 2023๋ ๋ฐ์ดํฐ๋ง ์ ํํ์ธ์.
df = pd.DataFrame({
'year': [2022, 2022, 2023, 2023, 2022, 2023],
'quarter': ['Q1', 'Q2', 'Q1', 'Q2', 'Q3', 'Q3'],
'sales': [100, 120, 150, 180, 110, 200]
}).set_index(['year', 'quarter'])
# Solution
print(df.loc[2023])
Problem 2: ์๊ณ์ด ๋ฆฌ์ํ๋ง¶
์ผ๋ณ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ฐ ํ๊ท ์ผ๋ก ๋ฆฌ์ํ๋งํ์ธ์.
dates = pd.date_range('2023-01-01', periods=30, freq='D')
ts = pd.Series(np.random.randn(30), index=dates)
# Solution
weekly_avg = ts.resample('W').mean()
print(weekly_avg)
Problem 3: ์ด๋ ํ๊ท ¶
7์ผ ์ด๋ ํ๊ท ์ ๊ณ์ฐํ๊ณ ์๋ณธ๊ณผ ํจ๊ป ํ์ํ์ธ์.
dates = pd.date_range('2023-01-01', periods=30, freq='D')
ts = pd.Series(np.random.randn(30).cumsum(), index=dates)
# Solution
df = pd.DataFrame({
'original': ts,
'ma_7': ts.rolling(window=7).mean()
})
print(df)
Summary¶
| Feature | Functions/Methods |
|---|---|
| ๋ฉํฐ์ธ๋ฑ์ค | MultiIndex.from_*(), xs(), swaplevel(), stack(), unstack() |
| ์๊ณ์ด | to_datetime(), date_range(), resample(), rolling(), ewm() |
| ๋ฒ์ฃผํ | Categorical(), astype('category'), cat ์ ๊ทผ์ |
| ๋ฌธ์์ด | str ์ ๊ทผ์, ์ ๊ทํํ์, extract(), split() |
| ์ฑ๋ฅ | ๋ฒกํฐํ ์ฐ์ฐ, eval(), query(), ์ฒญํฌ ์ฒ๋ฆฌ |
| ํ์ดํ๋ผ์ธ | pipe(), ๋ฉ์๋ ์ฒด์ด๋ |