(파이썬) Pandas라이브러리(DataFrame) 2번째
<산술 연산과 데이터 정렬>
두 객체를 더할 때 짝이 맞지 않는 색인은 Na값으로 출력된다.
예)
s1+s2
a 5.2
c 1.1
d Nan
e 0.0
f NaN
g NaN
**DataFrame에서는 로우와 칼럼 모두에 적용된다.
예)
df1=DataFrame(np.arange(9.).reshape((3,3)), columns=list('bcd'),index=['Ohio','Texas','Colorado'])
df2=DataFrame(np.arange(9.).reshape((3,3)), columns=list('bde'),index=['Utah','Ohio','Texas','Oregon'])
<산술연산 메소드에 채워 넣을 값 지정하기>
위의 예에서
df1.add(df2, fill_value=0)
-->NaN으로 채워지는 대신 0으로 채워진다.
**Series나 DataFrame을 재색인 할 때 역시 fill_value를 지정할 수 있다.
예)df1.reindex(column=df2.columns, fill_value=0)
<함수의 적용과 매핑>
예)
frame=DataFrame(np.random.randn(4,3), columns=list('bde')
index=['Utah','Ohio','Texas','Oregon'])
np.abs(frame)-->모든 값이 양수로 된다.
**DataFrame의 apply메소드를 통해 수행할 수 있다.
f=lamda x: x.max()- x.min()
frame.apply(f)--->열들을 기준으로
frame.apply(f, axis=1)--->행들을 기준으로
예)
def f(x):
return Series([x.min(), x.max()], index=['min','max'])
frame.apply(f)
출력:
b d e
min 3 5 2
max 5 6 7
**frame객체에서 실수 값을 문자열 포맷으로 변환하고 싶다면 applymap을 이용한다.
예)
format=lamda x: '%.2f' x
frame.applymap(format)
이 메소드의 이름이 applymap인 이유는 Series가 각 원소에 적용할 함수를 지정하기 위한 map메소드를 가지고 있기 때문이다.
예) frame['e'].map(format)--->'e'는 열 이다.
<정렬과 순위>
1. sort_index()
예) obj.sort_index(): 로우나 칼럼의 색인을 알파벳 순으로 정렬
obj.sort_index(axis=1)
**데이터는 기본적으로 오름차순으로 정렬되지만 내림차순으로 정렬할 수 있다.
예) frame.sort_index(axis=1, ascending=False)
**Series객체를 값에 따라 정렬하고 싶으면 order메소드를 사용한다.
예)
obj=Series([4,7,-3,2])
obj.order()
**정렬할 때 기본적으로 비어있는 값은 Series객체에서 가장 마지막에 위치한다.
**DataFrame에서는 하나 이상의 칼럼에 있는 값으로 정렬이 필요할 수 있다. 이럴때는 by옵션에 필요한 칼럼의 이름을 넘기면 된다.
예) frame=DataFrame({'b':[4,7,-3,2] , 'a':[0,1,0,1]})
frame.sort_index(by='b')
출력 결과:
a b a b
0 0 4 2 0 -3
1 1 7 3 1 2
2 0 -3 0 0 4
3 1 2 1 1 7
**여러개의 칼럼을 정렬하려면
frame.sort_index(by=['a','b'])
<중복 색인>
obj=Series(range(5), index=['a','a','b','b','c'])
**is_unique속성은 해당 값이 유일한지 아닌지 알려준다.
예)
obj.index.is_unique
-->False
<<기술통계 계산과 요약>>
pandas객체는 일반적인 수학메소드와 통계 메소드를 가지고 있다.
예)
df=DataFrame([[1.4, np.nan],[7.1, -4.5],[np.nan, np.nan],[0.75, -1.3]],
index=['a','b','c','d'], columns=['one','two'])
**sum메소드를 호출하면 각 칼럼의 합을 담은 Series를 반환한다.
예)
df.sum()
one: 9.25
two: -5.80
**axis=1 옵션을 넘기면 각 로우의 합을 반환한다.
예)
df.sum(axis=1)-->axis가 0이면 로우 1이면 칼럼이다.
a 1.40
b 2.60
c NaN
d -0.55
**전체로우나 칼럼의 값이 NA가 아니라면 계산 과정에서 NA값은 제외시키고 계산된다. 이는 skipna옵션을 통해 조정할 수 있다.
예)
df.mean(axis=1, skipna=False)
출력:
a NaN
b 1.300
c NaN
d -0.275
**idxmin()이나 idxmax()같은 메소드는 최소혹은 최대 값을 가지고 있는 색인 값 같은 간접 통계 를 반환한다.
예)
df.idxmax()
출력:
one b
two d
**또 다른 메소드는 누산이 있다.
예)
df.cumsum()
one two
a 1.40 NaN
b 8.50 -4.5
c NaN NaN
d 9.25 -5.8
**describe메소드는 한번에 통계 결과를 여러 개 만들어 낸다.
예)
df.describe()
one two
count
mean
std
min
25%
50%
75%
max
**수치 데이터가 아니면 다른 통계 데이터를 생성한다.
예)
obj=Series(['a','a','b','c']*4)
obj.describe()
결과:
count 16
unique 3
top a
freq 8
<기술 통계와 요약 통계 메소드>
argmin, argmax: 각각 최소, 최대 값을 갖고 있는 색인의 위치(정수)를 반환한다.
quantile: 0부터 1까지 분위수를 계산한다.
median: 중간 값(50%분위)을 반환한다.
mad: 평균값에서 절대 평균 편차를 뺀다.
var: 표본분산의 값을 구한다.
std: 표본 정규 분산의 값을 구한다.
diff: 1차 산술 차를 구한다(시계열 데이터에서 유용)
pct_change: 퍼센트 변화율을 계산한다.
<상관관계와 공분산>
** 금융사이트에서 구한 주식가격과 시가총액을 담ㄱ있는 DataFrame에 대해 생각해보자
예)
import pandas.io.data as web
all_data={}
for ticker in ['APPL','IBM', 'MSFT', 'GOOG']
all_data[ticker]=web.get_data_yahoo(ticker,'1/1/2000','1/1/2010')
price=dataFrame({tic: data['Adj Close']
for tic,data in all_data.iteritems()})
volume=dataFrame({tic: data['Volume']
for tic,data in all_data.iteritems()})
**이제 각 주식의 퍼센트 변화율을 계산해보자
returns=price.pct_change()
returns.tail()
**corr메소드는 NA가 아니고 정렬된 색인에서 연속하는 두 Series에 대해 상관관계를 계산하고 cov메소드는 공분산을 계산한다.
예)
returns.MSFT.corr(returns.IBM)
returns.MSFT.cov(returns.IBM)
rerturns.corr()
returns.cov()
<유일 값, 값 세기, 멤버십>
**value_counts()는 도수를 계산해서 반환한다.
예)
obj=Series(['c','a','d','a','a','b','b','c','c'])
obj.value_counts()
결과:
c 3
a 3
b 2
d 1
**또한 value_counts메소드는 pandas의 최상위 메소드로 어떤 배열이나 순차 자료 구조에서도 사용할 수 있다.
예)pd.value_counts(obj.value,sort=False)
**isin메소드는 어떤 값이 Series에 있는지 나타내는 불리언 벡터를 반환하는데, Series나 Dataframe의 칼럼에서 값을 골라내고 싶을때 유용하다.
예)
mask=obj.isin(['b','c'])
mask는 불리언 값이 다 출력
obj[mask]는 True인 인덱스와 그 인덱스의 값들이 출력된다.
**DataFrame의 여러 로우에 대해 히스토그램을 구해야 하는 경우가 있다.
예)
data=DataFrame({'Qu1': [1,3,4,3,4],
'Qu2': [2,3,1,2,3],
'Qu3': [1,5,2,4,4]})
DataFrame의 apply함수에 pandas.value_counts를 넘기면 다음과 같은 결과를 얻을 수 있다.
value_counts메소드의 결과가 DataFrame의 칼럼크기보다 작을 수 있기 때문에 fillna(0)함수를 이용해서 비어있는 값은 0으로채운다.
result=data.apply(pd.value_counts).fillna(0)
<누락된 데이터 처리하기>
파이썬의 내장 None값 또한 NA로 처리한다.
예)
string_data[0]=None
string_data.isnull()
0번째는 True가 된다.
<NA처리 메소드>
dropna: 누락된 데이터가 있는 축(로우,칼럼)을 제외 시킨다. 어느 정도의 누락 데이터까지 용인 할것인지 지정할 수 있다.
fillna: 누락된 데이터를 대신할 값을 'ffill'또는 'bfill'같은 보간 메소드를 적용한다.
isnull: 누락되거나 NA인 값을 알려주는 불리언 값이 저장된, 같은 형의 객체를 반환한다.
notnull: isnull과 반대다.
<누락된 데이터 골라내기>
예)
from numpy import nan as NA
data=Series([1, NA, 3.5, NA, 7])
data.dropna()
또는 data[data.notnull()]
** DataFrame의 객체는조금 복잡한데, 모두 NA인 로우나 칼럼을 제외하든가 하나라도 NA인 값을 포함하고 있는 로우나 칼럼을 제외시킬 수도 있따. dropna는 기본적으로 NA값이 하나라도 있는 로우는 제외시킨다.
에)
data.dropna()
**how='all'옵션을 주면 모든 값이 NA인 로우만 제외시킨다.
data.dropna(how='all')
**axis=1을주면 칼럼만 제외시킨다.
예)
data.dropna(axis=1, how='all')
**DataFrame의 로우를 제외시키는 방법은 주로 시계열 데이터에 사용되는 경향이있다. 몇개 이상의 값이 들어잇는 로우만 살펴보고 싶다면 thresh인자에 원하는 값을 넘기면 된다.
예)
df=DataFrame(np.random.randn(7,3))
df.ix[:4,1]=NA;
df.ix[:2,2]=NA
df.dropna(thresh=3)를 하면 세개의 칼럼에 모두 값이 들어있는 인덱스만 출력한다.
<누락된 값 채우기>
**fillna메소드는 누락된 구멍을 메꿔준다
예)
df.fillna(0)
**fillna에 사전값을 넘겨서 각 칼럼마다 다른 값을 채워 넣을 수 있다.
예)
df.fillna({1: 0.5, 3:-1})
--->1칼럼에는 0.5 3칼럼에는 -1이 채워진다.
**fillna는 새로운 객체를 반환하지만 다음처럼 기존 객체를 변경할 수 있따.
예)
a=df.fillna(0,inplace=True)
--->df객체에 누락된 값이 0으로 채워지고 객체도 반환한다.
**재색인에서 사용 가능한 보간 메소드는 fillna메소드에서 사용이 가능하다.
예)
df.fillna(method='ffill')
df.fillna(method='ffill',limit=2)-->limit은 값을 앞 혹은 뒤에서부터 몇개 까지 채울지를 지정한다.
**조금 창의적으로 생각하면 fillna를 이용해서 다양한 일을 할 수 있다..
예를들어 Series의 평균값이나 중간 값을 전달할 수 있다.
data=Series([1., NA, 3.5, NA, 7])
data.fillna(data.mean())
<계층적 색인>
축에 대해 다중(둘 이상) 색인 단계를 지정할 수 있도록 해준다. 약간 추상적으로 말하면 높은(고차원) 데이터를 낮은 차원의 형식으로 다룰 수 있게 해주는 기능이다.
예)
data=Series(np.random.randn(10), index=[['a','a','a','b','b','b','c','c','c','d','d'],
[1,2,3,1,2,3,1,2,2,3]])
a 1 0.67
2 0.85
3 -0.9
b 1
2
3
c 1
2
d 2
3
예) data.index
[('a',1) ('a',2) ('a',3) ('b',1) ('b',2) ('b',3) ('c',1) ('c',2) ('d',2) ('d',3)]
**계층적으로 색인된 객체는 데이터의 부분집합을 부분적 색인으로 접근하는 것이 가능하다
예) data['b']
출력:
1 -0.02
2 -2.3
3 -0.6
예) data['b':'c']
출력:
b 1
2
3
c 1
2
예) data.ix[['b':'d']]
b 1
2
3
d 2
3
**하위 계층의 객체를 선택하는 것도 가능하다.
data[: ,2]
출력:
a 0.85
b -2.3
c -1.3
d 1.07
** 계층적인 색인은 데이터를 재형성하고 피벗 테이블 생성 같은 그룹 기반의 작업을 할 때 중요하게 사용된다. 예를 들어 위에서 만든 DataFrame객체에 unstack메소드를 사용해서 데이터를 새롭게 배열할 수 도있다.
예) data.unstack()
1 2 3
a
b
c
d
<DataFrame의 칼럼 사용하기>
두 객체를 더할 때 짝이 맞지 않는 색인은 Na값으로 출력된다.
예)
s1+s2
a 5.2
c 1.1
d Nan
e 0.0
f NaN
g NaN
**DataFrame에서는 로우와 칼럼 모두에 적용된다.
예)
df1=DataFrame(np.arange(9.).reshape((3,3)), columns=list('bcd'),index=['Ohio','Texas','Colorado'])
df2=DataFrame(np.arange(9.).reshape((3,3)), columns=list('bde'),index=['Utah','Ohio','Texas','Oregon'])
<산술연산 메소드에 채워 넣을 값 지정하기>
위의 예에서
df1.add(df2, fill_value=0)
-->NaN으로 채워지는 대신 0으로 채워진다.
**Series나 DataFrame을 재색인 할 때 역시 fill_value를 지정할 수 있다.
예)df1.reindex(column=df2.columns, fill_value=0)
<함수의 적용과 매핑>
예)
frame=DataFrame(np.random.randn(4,3), columns=list('bde')
index=['Utah','Ohio','Texas','Oregon'])
np.abs(frame)-->모든 값이 양수로 된다.
**DataFrame의 apply메소드를 통해 수행할 수 있다.
f=lamda x: x.max()- x.min()
frame.apply(f)--->열들을 기준으로
frame.apply(f, axis=1)--->행들을 기준으로
예)
def f(x):
return Series([x.min(), x.max()], index=['min','max'])
frame.apply(f)
출력:
b d e
min 3 5 2
max 5 6 7
**frame객체에서 실수 값을 문자열 포맷으로 변환하고 싶다면 applymap을 이용한다.
예)
format=lamda x: '%.2f' x
frame.applymap(format)
이 메소드의 이름이 applymap인 이유는 Series가 각 원소에 적용할 함수를 지정하기 위한 map메소드를 가지고 있기 때문이다.
예) frame['e'].map(format)--->'e'는 열 이다.
<정렬과 순위>
1. sort_index()
예) obj.sort_index(): 로우나 칼럼의 색인을 알파벳 순으로 정렬
obj.sort_index(axis=1)
**데이터는 기본적으로 오름차순으로 정렬되지만 내림차순으로 정렬할 수 있다.
예) frame.sort_index(axis=1, ascending=False)
**Series객체를 값에 따라 정렬하고 싶으면 order메소드를 사용한다.
예)
obj=Series([4,7,-3,2])
obj.order()
**정렬할 때 기본적으로 비어있는 값은 Series객체에서 가장 마지막에 위치한다.
**DataFrame에서는 하나 이상의 칼럼에 있는 값으로 정렬이 필요할 수 있다. 이럴때는 by옵션에 필요한 칼럼의 이름을 넘기면 된다.
예) frame=DataFrame({'b':[4,7,-3,2] , 'a':[0,1,0,1]})
frame.sort_index(by='b')
출력 결과:
a b a b
0 0 4 2 0 -3
1 1 7 3 1 2
2 0 -3 0 0 4
3 1 2 1 1 7
**여러개의 칼럼을 정렬하려면
frame.sort_index(by=['a','b'])
<중복 색인>
obj=Series(range(5), index=['a','a','b','b','c'])
**is_unique속성은 해당 값이 유일한지 아닌지 알려준다.
예)
obj.index.is_unique
-->False
<<기술통계 계산과 요약>>
pandas객체는 일반적인 수학메소드와 통계 메소드를 가지고 있다.
예)
df=DataFrame([[1.4, np.nan],[7.1, -4.5],[np.nan, np.nan],[0.75, -1.3]],
index=['a','b','c','d'], columns=['one','two'])
**sum메소드를 호출하면 각 칼럼의 합을 담은 Series를 반환한다.
예)
df.sum()
one: 9.25
two: -5.80
**axis=1 옵션을 넘기면 각 로우의 합을 반환한다.
예)
df.sum(axis=1)-->axis가 0이면 로우 1이면 칼럼이다.
a 1.40
b 2.60
c NaN
d -0.55
**전체로우나 칼럼의 값이 NA가 아니라면 계산 과정에서 NA값은 제외시키고 계산된다. 이는 skipna옵션을 통해 조정할 수 있다.
예)
df.mean(axis=1, skipna=False)
출력:
a NaN
b 1.300
c NaN
d -0.275
**idxmin()이나 idxmax()같은 메소드는 최소혹은 최대 값을 가지고 있는 색인 값 같은 간접 통계 를 반환한다.
예)
df.idxmax()
출력:
one b
two d
**또 다른 메소드는 누산이 있다.
예)
df.cumsum()
one two
a 1.40 NaN
b 8.50 -4.5
c NaN NaN
d 9.25 -5.8
**describe메소드는 한번에 통계 결과를 여러 개 만들어 낸다.
예)
df.describe()
one two
count
mean
std
min
25%
50%
75%
max
**수치 데이터가 아니면 다른 통계 데이터를 생성한다.
예)
obj=Series(['a','a','b','c']*4)
obj.describe()
결과:
count 16
unique 3
top a
freq 8
<기술 통계와 요약 통계 메소드>
argmin, argmax: 각각 최소, 최대 값을 갖고 있는 색인의 위치(정수)를 반환한다.
quantile: 0부터 1까지 분위수를 계산한다.
median: 중간 값(50%분위)을 반환한다.
mad: 평균값에서 절대 평균 편차를 뺀다.
var: 표본분산의 값을 구한다.
std: 표본 정규 분산의 값을 구한다.
diff: 1차 산술 차를 구한다(시계열 데이터에서 유용)
pct_change: 퍼센트 변화율을 계산한다.
<상관관계와 공분산>
** 금융사이트에서 구한 주식가격과 시가총액을 담ㄱ있는 DataFrame에 대해 생각해보자
예)
import pandas.io.data as web
all_data={}
for ticker in ['APPL','IBM', 'MSFT', 'GOOG']
all_data[ticker]=web.get_data_yahoo(ticker,'1/1/2000','1/1/2010')
price=dataFrame({tic: data['Adj Close']
for tic,data in all_data.iteritems()})
volume=dataFrame({tic: data['Volume']
for tic,data in all_data.iteritems()})
**이제 각 주식의 퍼센트 변화율을 계산해보자
returns=price.pct_change()
returns.tail()
**corr메소드는 NA가 아니고 정렬된 색인에서 연속하는 두 Series에 대해 상관관계를 계산하고 cov메소드는 공분산을 계산한다.
예)
returns.MSFT.corr(returns.IBM)
returns.MSFT.cov(returns.IBM)
rerturns.corr()
returns.cov()
<유일 값, 값 세기, 멤버십>
**value_counts()는 도수를 계산해서 반환한다.
예)
obj=Series(['c','a','d','a','a','b','b','c','c'])
obj.value_counts()
결과:
c 3
a 3
b 2
d 1
**또한 value_counts메소드는 pandas의 최상위 메소드로 어떤 배열이나 순차 자료 구조에서도 사용할 수 있다.
예)pd.value_counts(obj.value,sort=False)
**isin메소드는 어떤 값이 Series에 있는지 나타내는 불리언 벡터를 반환하는데, Series나 Dataframe의 칼럼에서 값을 골라내고 싶을때 유용하다.
예)
mask=obj.isin(['b','c'])
mask는 불리언 값이 다 출력
obj[mask]는 True인 인덱스와 그 인덱스의 값들이 출력된다.
**DataFrame의 여러 로우에 대해 히스토그램을 구해야 하는 경우가 있다.
예)
data=DataFrame({'Qu1': [1,3,4,3,4],
'Qu2': [2,3,1,2,3],
'Qu3': [1,5,2,4,4]})
DataFrame의 apply함수에 pandas.value_counts를 넘기면 다음과 같은 결과를 얻을 수 있다.
value_counts메소드의 결과가 DataFrame의 칼럼크기보다 작을 수 있기 때문에 fillna(0)함수를 이용해서 비어있는 값은 0으로채운다.
result=data.apply(pd.value_counts).fillna(0)
<누락된 데이터 처리하기>
파이썬의 내장 None값 또한 NA로 처리한다.
예)
string_data[0]=None
string_data.isnull()
0번째는 True가 된다.
<NA처리 메소드>
dropna: 누락된 데이터가 있는 축(로우,칼럼)을 제외 시킨다. 어느 정도의 누락 데이터까지 용인 할것인지 지정할 수 있다.
fillna: 누락된 데이터를 대신할 값을 'ffill'또는 'bfill'같은 보간 메소드를 적용한다.
isnull: 누락되거나 NA인 값을 알려주는 불리언 값이 저장된, 같은 형의 객체를 반환한다.
notnull: isnull과 반대다.
<누락된 데이터 골라내기>
예)
from numpy import nan as NA
data=Series([1, NA, 3.5, NA, 7])
data.dropna()
또는 data[data.notnull()]
** DataFrame의 객체는조금 복잡한데, 모두 NA인 로우나 칼럼을 제외하든가 하나라도 NA인 값을 포함하고 있는 로우나 칼럼을 제외시킬 수도 있따. dropna는 기본적으로 NA값이 하나라도 있는 로우는 제외시킨다.
에)
data.dropna()
**how='all'옵션을 주면 모든 값이 NA인 로우만 제외시킨다.
data.dropna(how='all')
**axis=1을주면 칼럼만 제외시킨다.
예)
data.dropna(axis=1, how='all')
**DataFrame의 로우를 제외시키는 방법은 주로 시계열 데이터에 사용되는 경향이있다. 몇개 이상의 값이 들어잇는 로우만 살펴보고 싶다면 thresh인자에 원하는 값을 넘기면 된다.
예)
df=DataFrame(np.random.randn(7,3))
df.ix[:4,1]=NA;
df.ix[:2,2]=NA
df.dropna(thresh=3)를 하면 세개의 칼럼에 모두 값이 들어있는 인덱스만 출력한다.
<누락된 값 채우기>
**fillna메소드는 누락된 구멍을 메꿔준다
예)
df.fillna(0)
**fillna에 사전값을 넘겨서 각 칼럼마다 다른 값을 채워 넣을 수 있다.
예)
df.fillna({1: 0.5, 3:-1})
--->1칼럼에는 0.5 3칼럼에는 -1이 채워진다.
**fillna는 새로운 객체를 반환하지만 다음처럼 기존 객체를 변경할 수 있따.
예)
a=df.fillna(0,inplace=True)
--->df객체에 누락된 값이 0으로 채워지고 객체도 반환한다.
**재색인에서 사용 가능한 보간 메소드는 fillna메소드에서 사용이 가능하다.
예)
df.fillna(method='ffill')
df.fillna(method='ffill',limit=2)-->limit은 값을 앞 혹은 뒤에서부터 몇개 까지 채울지를 지정한다.
**조금 창의적으로 생각하면 fillna를 이용해서 다양한 일을 할 수 있다..
예를들어 Series의 평균값이나 중간 값을 전달할 수 있다.
data=Series([1., NA, 3.5, NA, 7])
data.fillna(data.mean())
<계층적 색인>
축에 대해 다중(둘 이상) 색인 단계를 지정할 수 있도록 해준다. 약간 추상적으로 말하면 높은(고차원) 데이터를 낮은 차원의 형식으로 다룰 수 있게 해주는 기능이다.
예)
data=Series(np.random.randn(10), index=[['a','a','a','b','b','b','c','c','c','d','d'],
[1,2,3,1,2,3,1,2,2,3]])
a 1 0.67
2 0.85
3 -0.9
b 1
2
3
c 1
2
d 2
3
예) data.index
[('a',1) ('a',2) ('a',3) ('b',1) ('b',2) ('b',3) ('c',1) ('c',2) ('d',2) ('d',3)]
**계층적으로 색인된 객체는 데이터의 부분집합을 부분적 색인으로 접근하는 것이 가능하다
예) data['b']
출력:
1 -0.02
2 -2.3
3 -0.6
예) data['b':'c']
출력:
b 1
2
3
c 1
2
예) data.ix[['b':'d']]
b 1
2
3
d 2
3
**하위 계층의 객체를 선택하는 것도 가능하다.
data[: ,2]
출력:
a 0.85
b -2.3
c -1.3
d 1.07
** 계층적인 색인은 데이터를 재형성하고 피벗 테이블 생성 같은 그룹 기반의 작업을 할 때 중요하게 사용된다. 예를 들어 위에서 만든 DataFrame객체에 unstack메소드를 사용해서 데이터를 새롭게 배열할 수 도있다.
예) data.unstack()
1 2 3
a
b
c
d
<DataFrame의 칼럼 사용하기>
블로그 관리자가 댓글을 삭제했습니다.
답글삭제