이번 포스팅에서는 Python 의 Pandas 에서 함수를 적용할 때 사용하는 map(), applymap(), apply() 메소드에 대해서 알아보겠습니다. 

 

(1) map(): Series 에 대해 element-wise 로 함수 적용

(2) applymap(): DataFrame에 대해 element-wise 로 함수 적용

(3) apply(): DataFrame에 대해 행/열 (row/column) 로 함수 적용

 

 

Pandas 함수 적용하기: map, applymap, apply

 

 

(1) map(): Series 에 대해 element-wise 로 함수 적용

 

예제로 사용할 DataFrame을 만들어보겠습니다. 

 

import numpy as np
import pandas as pd

## making a sample pandas DataFrame
np.random.seed(1004)

df = pd.DataFrame(
    np.random.randn(4, 3), 
    columns=['x1', 'x2', 'x3'], 
    index=['A', 'B', 'C', 'D'])

print(df)
#          x1        x2        x3
# A  0.594403  0.402609 -0.805162
# B  0.115126 -0.753065 -0.784118
# C  1.461576  1.576076 -0.171318
# D -0.914482  0.860139  0.358802

 

 

 

map() 메소드는 pandas Series 에 대해 함수를 적용할 때 사용합니다. 아래 예제는 익명 함수 lambda 로 정의한 s_formater 함수를 map() 메소드로 Series에 적용해 본 것입니다. 

 

## pandas Series
df['x1']
# A    0.594403
# B    0.115126
# C    1.461576
# D   -0.914482
# Name: x1, dtype: float64


## map: applying an element-wise function for Series
s_formater = lambda x: '%.2f'% x

df['x1'].map(s_formater)
# A     0.59
# B     0.12
# C     1.46
# D    -0.91
# Name: x1, dtype: object

 

 

 

아래 예제에서는 Series의 인덱스를 키로 해서 매핑(mapping by index)하여 Series의 값을 변환한 것입니다. 

-------------------------------------------------------

s_1 인덱스 --> s_1 값 = s_2 인덱스  -->  s_2 값

-------------------------------------------------------

'a'             -->             0                    -->     'A'

'b'             -->             1                     -->     'B'

'c'             -->             2                     -->     'C'

-------------------------------------------------------

 

 

## 예제 pandas Series 만들기
s_1 = pd.Series({'a': 0, 'b': 1, 'c': 2})
s_2 = pd.Series({0: 'A', 1: 'B', 2: 'C'})

print(s_1)
# a    0
# b    1
# c    2
# dtype: int64

print(s_2)
# 0    A
# 1    B
# 2    C
# dtype: object


##-- 인덱스 키를 기준으로 매핑하기 (mapping by index on Series)
print(s_1.map(s_2))
# a    A
# b    B
# c    C
# dtype: object

 

 

 

(2) applymap(): DataFrame에 대해 element-wise 로 함수 적용

 

다음 예제는 applymap() 메소드를 사용해서 익명함수 lambda 로 정의한 s_formater 함수를 DataFrame 의 각 원소에 대해 적용한 것입니다. (map() 은 Series 대상 vs. applymap()은 DataFrame 대상 element-wise 함수 적용)

 

## applymap: applying an element-wise for DataFrame
s_formater = lambda x: '%.2f'% x

df_2 = df.applymap(s_formater)

print(df_2)
#       x1     x2     x3
# A   0.59   0.40  -0.81
# B   0.12  -0.75  -0.78
# C   1.46   1.58  -0.17
# D  -0.91   0.86   0.36

 

 

 

(3) apply(): DataFrame에 대해 행/열 (row/column) 로 함수 적용

 

아래 예제는 column 기준 (axis=0),  row 기준 (axis=1) 으로 익명함수 lambda로 정의한 (열 또는 행 기준, 최대값 - 최소값) 을 계산하는 함수를 apply() 메소드로 DataFrame에 대해 적용해본 것입니다. 

 

## apply: applying a function on row/column basis of a DataFrame
f = lambda x: x.max() - x.min()

## on column basis
df.apply(f, axis=0)

# x1    2.376058
# x2    2.329141
# x3    1.163964
# dtype: float64



## on row basis
df.apply(f, axis=1)

# A    1.399565
# B    0.899243
# C    1.747393
# D    1.774621
# dtype: float64

 

 

 

아래 예는 apply() 메소드를 써서 DataFrame에 행(row) 기준으로 최대값을 계산하는 익명함수 lambda로 정의한 함수를 적용해서 'x_max'라는 새로운 칼럼을 추가한 것입니다. 

 

## 새로운 칼럼 추가하기
df['x_max'] = df.apply(lambda x: x.max(), axis=1)

print(df)
#          x1        x2        x3     x_max
# A  0.594403  0.402609 -0.805162  0.594403
# B  0.115126 -0.753065 -0.784118  0.115126
# C  1.461576  1.576076 -0.171318  1.576076
# D -0.914482  0.860139  0.358802  0.860139


## equivalent
df['x_max'] = df.max(axis=1)

 

 

* Pandas DataFrame에서 여러개의 칼럼에 대해 그룹을 집계를 할 때 다른 집계 함수를 적용하는 방법은 https://rfriend.tistory.com/393 를 참고하세요. 

 

* Python의 익명 함수 lambda 에 대해서는 https://rfriend.tistory.com/366 를 참고하세요. 

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 샘플 크기가 다른 2개 이상의 집단에 대해 평균의 차이가 존재하는지를 검정하는 일원분산분석(one-way ANOVA)에 대해 scipy 모듈의 scipy.stats.f_oneway() 메소드를 사용해서 분석하는 방법(rfriend.tistory.com/638)을 소개하였습니다. 

 

이번 포스팅에서는 2개 이상의 집단에 대해 pandas DataFrame에 들어있는 여러 개의 숫자형 변수(one-way ANOVA for multiple numeric variables in pandas DataFrame) 별로 일원분산분석 검정(one-way ANOVA test)을 하는 방법을 소개하겠습니다. 

 

숫자형 변수와 집단 변수의 모든 가능한 조합을 MultiIndex 로 만들어서 statsmodels.api 모듈의 stats.anova_lm() 메소드의 모델에 for loop 순환문으로 변수를 바꾸어 가면서 ANOVA 검정을 하도록 작성하였습니다. 

 

 

 

먼저, 3개의 집단('grp 1', 'grp 2', 'grp 3')을 별로 'x1', 'x2', 'x3, 'x4' 의 4개의 숫자형 변수를 각각 30개씩 가지는 가상의 pandas DataFrame을 만들어보겠습니다. 이때 숫자형 변수는 모두 정규분포로 부터 난수를 발생시켜 생성하였으며, 'x3'와 'x4'에 대해서는 집단3 ('grp 3') 의 평균이 다른 2개 집단의 평균과는 다른 정규분포로 부터 난수를 발생시켜 생성하였습니다.  

 

아래의 가상 데이터셋은 결측값이 없이 만들었습니다만, 실제 기업에서 쓰는 데이터셋에는 혹시 결측값이 존재할 수도 있으므로 결측값을 없애거나 또는 결측값을 그룹 별 평균으로 대체한 후에 one-way ANOVA 를 실행하기 바랍니다. 

 

## Creating sample dataset
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# generate 90 IDs
id = np.arange(90) + 1

# Create 3 groups with 30 observations in each group.
from itertools import chain, repeat
grp = list(chain.from_iterable((repeat(number, 30) for number in [1, 2, 3])))

# generate random numbers per each groups from normal distribution
np.random.seed(1004)

# for 'x1' from group 1, 2 and 3
x1_g1 = np.random.normal(0, 1, 30)
x1_g2 = np.random.normal(0, 1, 30)
x1_g3 = np.random.normal(0, 1, 30)

# for 'x2' from group 1, 2 and 3
x2_g1 = np.random.normal(10, 1, 30)
x2_g2 = np.random.normal(10, 1, 30)
x2_g3 = np.random.normal(10, 1, 30)

# for 'x3' from group 1, 2 and 3
x3_g1 = np.random.normal(30, 1, 30)
x3_g2 = np.random.normal(30, 1, 30)
x3_g3 = np.random.normal(50, 1, 30) # different mean

x4_g1 = np.random.normal(50, 1, 30)
x4_g2 = np.random.normal(50, 1, 30)
x4_g3 = np.random.normal(20, 1, 30) # different mean

# make a DataFrame with all together
df = pd.DataFrame({'id': id, 
                   'grp': grp, 
                   'x1': np.concatenate([x1_g1, x1_g2, x1_g3]), 
                   'x2': np.concatenate([x2_g1, x2_g2, x2_g3]), 
                   'x3': np.concatenate([x3_g1, x3_g2, x3_g3]), 
                   'x4': np.concatenate([x4_g1, x4_g2, x4_g3])})
                   
df.head()
[Out] 

id	grp	x1	x2	x3	x4
0	1	1	0.594403	10.910982	29.431739	49.232193
1	2	1	0.402609	9.145831	28.548873	50.434544
2	3	1	-0.805162	9.714561	30.505179	49.459769
3	4	1	0.115126	8.885289	29.218484	50.040593
4	5	1	-0.753065	10.230208	30.072990	49.601211


df[df['grp'] == 3].head()
[Out] 

id	grp	x1	x2	x3	x4
60	61	3	-1.034244	11.751622	49.501195	20.363374
61	62	3	0.159294	10.043206	50.820755	19.800253
62	63	3	0.330536	9.967849	50.461775	20.993187
63	64	3	0.025636	9.430043	50.209187	17.892591
64	65	3	-0.092139	12.543271	51.795920	18.883919

 

 

 

가령, 'x3' 변수에 대해 집단별로 상자 그래프 (Box plot for 'x3' by groups) 를 그려보면, 아래와 같이 집단1과 집단2는 유사한 반면에 집단3은 평균이 차이가 많이 나게 가상의 샘플 데이터가 생성되었음을 알 수 있습니다. 

 

## Boxplot for 'x3' by 'grp'
plt.rcParams['figure.figsize'] = [10, 6]
sns.boxplot(x='grp', y='x3', data=df)
plt.show()

 

 

여러개의 변수에 대해 일원분산분석을 하기 전에, 먼저 이해를 돕기 위해 Python의 statsmodels.api 모듈의 stats.anova_lm() 메소드를 사용해서 'x1' 변수에 대해 집단(집단 1/2/3)별로 평균이 같은지 일원분산분석으로 검정을 해보겠습니다. 

 

    - 귀무가설(H0) : 집단1의 x1 평균 = 집단2의 x1 평균 = 집단3의 x1 평균

    - 대립가설(H1) : 적어도 1개 이상의 집단의 x1 평균이 다른 집단의 평균과 다르다. (Not H0)

 

# ANOVA for x1 and grp
import statsmodels.api as sm
from statsmodels.formula.api import ols

model = ols('x1 ~ grp', data=df).fit()
sm.stats.anova_lm(model, typ=1)
[Out]

df	sum_sq	mean_sq	F	PR(>F)
grp	1.0	0.235732	0.235732	0.221365	0.639166
Residual	88.0	93.711314	1.064901	NaN	NaN

 

일원분산분석 결과 F 통계량이 0.221365, p-value가 0.639 로서 유의수준 5% 하에서 귀무가설을 채택합니다. 즉, 3개 집단 간 x1의 평균의 차이는 없다고 판단할 수 있습니다. (정규분포 X ~ N(0, 1) 를 따르는 모집단으로 부터 무작위로 3개 집단의 샘플을 추출했으므로 차이가 없게 나오는게 맞겠습니다.)

 

 

한개의 변수에 대한 일원분산분석하는 방법을 알아보았으니, 다음으로는 3개 집단별로 여러개의 연속형 변수인 'x1', 'x2', 'x3', 'x4' 에 대해서 for loop 순환문으로 돌아가면서 일원분산분석을 하고, 그 결과를 하나의 DataFrame에 모아보도록 하겠습니다. 

 

(1) 먼저, 일원분산분석을 하려는 모든 숫자형 변수와 집단 변수에 대한 가능한 조합의 MultiIndex 를 생성해줍니다. 

 

# make a multiindex for possible combinations of Xs and Group
num_col = ['x1','x2', 'x3', 'x4']
cat_col =  ['grp']
mult_idx = pd.MultiIndex.from_product([num_col, cat_col],
                                   names=['x', 'grp'])

print(mult_idx)
[Out]
MultiIndex([('x1', 'grp'),
            ('x2', 'grp'),
            ('x3', 'grp'),
            ('x4', 'grp')],
           names=['x', 'grp'])
           

 

 

(2) for loop 순환문(for x, grp in mult_idx:)으로 model = ols('{} ~ {}'.format(x, grp) 의 선형모델의  y, x 부분의 변수 이름을 바꾸어가면서 sm.stats.anova_lm(model, typ=1) 로 일원분산분석을 수행합니다. 이렇게 해서 나온 일원분산분석 결과 테이블을 anova_tables.append(anova_table) 로 순차적으로 append 해나가면 됩니다.  

 

# ANOVA test for multiple combinations of X and Group
import statsmodels.api as sm
from statsmodels.formula.api import ols

anova_tables = []
for x, grp in mult_idx:
    model = ols('{} ~ {}'.format(x, grp), data=df).fit()
    anova_table = sm.stats.anova_lm(model, typ=1)
    anova_tables.append(anova_table)

df_anova_tables = pd.concat(anova_tables, keys=mult_idx, axis=0)

df_anova_tables
[Out]

df	sum_sq	mean_sq	F	PR(>F)
x1	grp	grp	1.0	0.235732	0.235732	0.221365	6.391661e-01
Residual	88.0	93.711314	1.064901	NaN	NaN
x2	grp	grp	1.0	0.448662	0.448662	0.415853	5.206912e-01
Residual	88.0	94.942885	1.078896	NaN	NaN
x3	grp	grp	1.0	6375.876120	6375.876120	259.202952	5.779374e-28
Residual	88.0	2164.624651	24.598007	NaN	NaN
x4	grp	grp	1.0	13760.538009	13760.538009	256.515180	8.145953e-28
Residual	88.0	4720.684932	53.644147	NaN	NaN

anova tables

 

 

만약 특정 변수에 대한 일원분산분석 결과만을 조회하고 싶다면, 아래처럼 DataFrame의 MultiIndex 에 대해 인덱싱을 해오면 됩니다. 가령, 'x3' 에 대한 집단별 평균 차이 여부를 검정한 결과는 아래처럼 인덱싱해오면 됩니다. 

 

## Getting values of 'x3' from ANOVA tables
df_anova_tables.loc[('x3', 'grp', 'grp')]
[Out]

df         1.000000e+00
sum_sq     6.375876e+03
mean_sq    6.375876e+03
F          2.592030e+02
PR(>F)     5.779374e-28
Name: (x3, grp, grp), dtype: float64

 

 

F 통계량과 p-value 에 대해서 조회하고 싶으면 위의 결과에서 DataFrame 의 칼럼 이름으로 선택해오면 됩니다. 

 

# F-statistic
df_anova_tables.loc[('x3', 'grp', 'grp')]['F']
[Out]
259.2029515179077


# P-value
df_anova_tables.loc[('x3', 'grp', 'grp')]['PR(>F)']
[Out]
5.7793742588216585e-28

 

 

 

MultiIndex 를 인덱싱해오는게 좀 불편할 수 도 있는데요, 이럴 경우  df_anova_tables.reset_index() 로  MultiIndex 를 칼럼으로 변환해서 사용할 수도 있습니다. 

# resetting index to columns
df_anova_tables_2 = df_anova_tables.reset_index().dropna()


df_anova_tables_2
[Out]

level_0	level_1	level_2	df	sum_sq	mean_sq	F	PR(>F)
0	x1	grp	grp	1.0	0.235732	0.235732	0.221365	6.391661e-01
2	x2	grp	grp	1.0	0.448662	0.448662	0.415853	5.206912e-01
4	x3	grp	grp	1.0	6375.876120	6375.876120	259.202952	5.779374e-28
6	x4	grp	grp	1.0	13760.538009	13760.538009	256.515180	8.145953e-28

 

 

Greenplum DB에서 PL/Python (또는 PL/R)을 사용하여 여러개의 숫자형 변수에 대해 일원분산분석을 분산병렬처리하는 방법(one-way ANOVA in parallel using PL/Python on Greenplum DB)은 rfriend.tistory.com/640 를 참고하세요. 

 

 

[reference] 

* ANOVA test using Python statsmodels
 
: https://www.statsmodels.org/stable/generated/statsmodels.stats.anova.anova_lm.html

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요! :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas DataFrame을 만들려고 할 때 "ValueError: If using all scalar values, you must pass an index" 에러 해결 방안 4가지를 소개하겠습니다.

아래의 예처럼 dictionary로 키, 값 쌍으로 된 데이터를 pandas DataFrame으로 만들려고 했을 때, 모든 값이 스칼라 값(if using all scalar values) 일 경우에 "ValueError: If using all scalar values, you must pass an index" 에러가 발생합니다. 

import pandas as pd

df = pd.DataFrame({'col_1': 1, 

                  'col_2': 2})

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-2-73d6f192ba2a> in <module>()
      1 df = pd.DataFrame({'col_1': 1, 
----> 2                   'col_2': 2})

~/anaconda3/envs/py3.5_tf1.4/lib/python3.5/site-packages/pandas/core/frame.py in __init__(self, data, index, columns, dtype, copy)
    273                                  dtype=dtype, copy=copy)
    274         elif isinstance(data, dict):
--> 275             mgr = self._init_dict(data, index, columns, dtype=dtype)
    276         elif isinstance(data, ma.MaskedArray):
    277             import numpy.ma.mrecords as mrecords

~/anaconda3/envs/py3.5_tf1.4/lib/python3.5/site-packages/pandas/core/frame.py in _init_dict(self, data, index, columns, dtype)
    409             arrays = [data[k] for k in keys]
    410 
--> 411         return _arrays_to_mgr(arrays, data_names, index, columns, dtype=dtype)
    412 
    413     def _init_ndarray(self, values, index, columns, dtype=None, copy=False):

~/anaconda3/envs/py3.5_tf1.4/lib/python3.5/site-packages/pandas/core/frame.py in _arrays_to_mgr(arrays, arr_names, index, columns, dtype)
   5494     # figure out the index, if necessary
   5495     if index is None:
-> 5496         index = extract_index(arrays)
   5497     else:
   5498         index = _ensure_index(index)

~/anaconda3/envs/py3.5_tf1.4/lib/python3.5/site-packages/pandas/core/frame.py in extract_index(data)
   5533 
   5534         if not indexes and not raw_lengths:
-> 5535             raise ValueError('If using all scalar values, you must pass'
   5536                              ' an index')
   5537 

ValueError: If using all scalar values, you must pass an index




이 에러를 해결하기 위한 4가지 방법을 차례대로 소개하겠습니다. 


 (1) 해결방안 1 : 인덱스 값을 설정해줌 (pass an index)

에러 메시지에 "you must pass an index" 라는 가이드라인대로 인덱스 값을 추가로 입력해주면 됩니다. 


# (1) pass an index

df = pd.DataFrame({'col_1': 1, 

                  'col_2': 2}

                  index = [0])


df

col_1col_2
012

 


물론 index 에 원하는 값을 입력해서 설정해줄 수 있습니다. index 에 'row_1' 이라고 해볼까요?

df = pd.DataFrame({'col_1': 1, 

                   'col_2': 2}

                  index = ['row_1'])


df

col_1col_2
row_112



 (2) 스칼라 값 대신 리스트 값을 입력 (use a list instead of scalar values)

입력하는 값(values)에 대괄호 [ ] 를 해주어서 리스트로 만들어준 값을 사전형의 값으로 사용하면 에러가 발생하지 않습니다. 

# (2) use a list instead of scalar values

df2 = pd.DataFrame({'col_1': [1]

                    'col_2': [2]})


df2

col_1col_2
012



 (3) pd.DataFrame.from_records([{'key': value}]) 를 사용해서 DataFrame 만들기

이때도 [ ] 로 해서 리스트 값을 입력해주어야 합니다. ( [ ] 빼먹으면 동일 에러 발생함)

# (3) use pd.DataFrame.from_records() with a list

df3 = pd.DataFrame.from_records([{'col_1': 1, 

                                  'col_2': 2}])


df3 

col_1col_2
012



 (4) pd.DataFrame.from_dict([{'key': value}]) 를 사용하여 DataFrame 만들기

(3)과 거의 유사한데요, from_records([]) 대신에 from_dict([]) 를 사용하였으며, 역시 [ ] 로 해서 리스트 값을 입력해주면 됩니다. 

# (4) use pd.DataFrame.from_dict([]) with a list

df4 = pd.DataFrame.from_dict([{'col_1': 1, 

                              'col_2': 2}])


df4

col_1col_2
012


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)


728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas의 DataFrame, Series 에서 특정 칼럼 내에 특정 값을 가지고 있는 행 전체를 indexing 해오는 방법 2가지를 소개하겠습니다. 

(1) df.isin() 메소드를 이용한 DataFrame, Series 값 indexing 방법

(2) 비교 조건문 boolean 을 이용한 DataFrame, Series 값 indexing 방법

먼저 간단한 예제로 사용할 DataFrame을 만들어보겠습니다. 

In [1]: import pandas as pd


In [2]: df = pd.DataFrame({'id': ['a', 'b', 'c', 'd', 'e', 'f'],

   ...: 'var': [1, 2, 3, 4, 5, 6]})


In [3]: df

Out[3]:

id var

0 a 1

1 b 2

2 c 3

3 d 4

4 e 5

5 f 6



 (1) df.isin() 메소드를 이용한 DataFrame, Series 값 indexing 방법

pandas DataFrame이나 Series에 isin() 메소드를 사용하면 isin() 메소드 안의 값이 들어 있으면, 즉 소속이 되어 있으면 (membership) True를, 들어있지 않으면 False 를 반환합니다. 

In [4]: df['id'].isin(['b', 'e', 'k'])

Out[4]:

0 False

1 True

2 False

3 False

4 True

5 False

Name: id, dtype: bool 


이처럼 조건 값의 소속 여부를 Boolean 값으로 반환해주는 점을 이용하여, 특정 값이 들어있는 행을 DataFrame, Series에서 indexing 해올 수 있습니다.  위의 예제 'df' DataFrame의 'id' 칼럼에서 'b', 'e', 'k' 값이 들어있는 행 전체를 가져와 보겠습니다. 

In [5]: df[df['id'].isin(['b', 'e', 'k'])]

Out[5]:

id var

1 b  2
4 e  5 


만약 'id'라는 칼럼 혹은 'var'라는 칼럼 중에서 특정 값이 어느 한군데라도(OR) 소속이 되어있으면 행을 가져와 보겠습니다. 

In [6]: df[df['id'].isin(['b', 'e', 'k']) | df['var'].isin([1, 8])]

   ...:

Out[6]:

id var

0 a 1

1 b 2
4 e 5 



 (2) 비교 조건문 boolean 을 이용한 DataFrame, Series 값 indexing 방법

위의 isin() 메소드를 이용한 [6]번째 실행 셀의 결과와 동일한 값을 indexing 해오는 것을, 이번에는 조건문 boolean 을 이용해서 해보겠습니다. 아무래도 위의 [6]번 isin() 메소드를 썼을 때보다 '|'(OR)를 모든 비교 조건문을 연결하다 보니 코드가 더 길고 복잡합니다. 

따라서, 특정 값이 포함/ 소속 (Membership) 여부를 조건으로 해서 DataFrame, Series로부터 행 전체를 indexing해와야 하는 경우 isin() 메소드를 유용하게 사용할 수 있습니다. (물론 아래의 비교 조건문의 경우 단지 포함/소속 여부 많이 아닌 모든 조건문에 범용적으로 사용할 수 있는 장점이 있습니다.)

In [7]: df[(df['id'] == 'b') | (df['id'] == 'e') | (df['id'] == 'k') | (df['var'] == 1) | (df['var'] == 8)]

Out[7]:

id var

0 a 1

1 b 2
4 e 5



 TypeError: cannot compare a dtyped [object] array with a scalar of type [bool] 

참고로, 여러개의 비교 조건문을 & (AND), 또는 | (OR) 로 연결해서 다수개의 조건을 AND, 또는 OR로 만족하는 행을 가져오고 싶을 경우 반드시 조건문에 (조건문) & (조건문), (조건문) | (조건문) 처럼 조건문에 괄호 ( ) 를 꼭 쳐줘야 합니다. (Be sure to include the parentheses in the conditions)

In [8]: df[df['id'] == 'b' | df['id'] == 'e' | df['id'] == 'k']

Traceback (most recent call last):


File "<ipython-input-8-3140416d729c>", line 1, in <module>

df[df['id'] == 'b' | df['id'] == 'e' | df['id'] == 'k']


File "C:\Users\admin\Anaconda3\lib\site-packages\pandas\core\ops.py", line 836, in wrapper

na_op(self.values, other),


File "C:\Users\admin\Anaconda3\lib\site-packages\pandas\core\ops.py", line 807, in na_op

x.dtype, type(y).__name__))


TypeError: cannot compare a dtyped [object] array with a scalar of type [bool]


Traceback (most recent call last):


File "<ipython-input-8-3140416d729c>", line 1, in <module>

df[df['id'] == 'b' | df['id'] == 'e' | df['id'] == 'k']


File "C:\Users\admin\Anaconda3\lib\site-packages\pandas\core\ops.py", line 836, in wrapper

na_op(self.values, other),


File "C:\Users\admin\Anaconda3\lib\site-packages\pandas\core\ops.py", line 807, in na_op

x.dtype, type(y).__name__))


TypeError: cannot compare a dtyped [object] array with a scalar of type [bool]


TypeError가 안나게 제대로 조건문 boolean indexing을 하려면 아래처럼 비교 조건문별로 '(비교 조건문) | (비교 조건문) 처럼 괄호 ( ) 를 쳐주면 됩니다. 

In [9]: df[ (df['id'] == 'b') | (df['id'] == 'e') | (df['id'] == 'k')]

Out[9]:

id var

1 b 2
4 e 5 


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas의 DataFrame을 Postgresql, Greenplum DB에 Copy 해서 집어넣는 방법을 소개하겠습니다. 

먼저 간단한 예제 pandas DataFrame을 만들어보겠습니다. 


import numpy as np

import pandas as pd


# make a DataFrame

school = pd.DataFrame({'region': ['gangnam', 'secho', 'bundang', 'mokdong'], 

                       'student_cnt': [100, 120, 150, 90], 

                       'math_score': [91, 95, 92, 93]}, 

                        columns=['region', 'student_cnt', 'math_score'])


school

Out[02]: 

    region  student_cnt  math_score

0  gangnam          100          91

1    secho          120          95

2  bundang          150          92

3  mokdong           90          93



이제 school 이라는 pandas DataFrame을 아래의 순서대로 DB에 Copy해서 넣어보겠습니다. 

(1) DataFrame을 CSV 파일로 내보내기 (export a DataFrame to CSV file)

(2) Postgresql, Greenplum DB에 연결하고 Table 만들기

(3) Postgresql, Greenplum DB의 Table에 CSV file을 Copy해서 집어넣기


  (1) DataFrame을 CSV 파일로 내보내기 (export a DataFrame to CSV file)


pandas의 to_csv() 메소드를 이용하였으며, index와 header 옵션은 False로 설정해서 CSV 파일에는 포함시키지 않도록 하겠습니다. 


school.to_csv('C:/Users/admin/Documents/data/school.csv', 

              sep=",", 

              na_rep="NaN", 

              index=False

              header=False)

 



  (2) Postgresql, Greenplum DB에 연결하고 Table 만들기

psycopg2 라이브러리를 이용해서 Postgresql, Greenplum DB에 연결해보겠습니다. 아래의 connect() 에는 본인의 DB 설정 정보를 바꾸어서 입력해주면 됩니다. 


# Postgresql DB connect using psycopg2

from psycopg2 import connect

conn = connect(host='localhost',  # set yours

               port=5432, 

               database='postgres', 

               user='postgres', 

               password='postgres')


cur = conn.cursor()


# Create a table at Postgresql public schema with school name

cur.execute("""

    DROP TABLE IF EXISTS school;

    CREATE TABLE school (

        region varchar(100), 

        student_cnt numeric, 

        math_score numeric

    )

""")

conn.commit()

 



  (3) Postgresql, Greenplum DB의 Table에 CSV file을 Copy해서 집어넣기

with open() 으로 로컬에 저장해놓은 school.csv 파일을 읽고, cursor.copy_expert() 를 이용하여 "COPY school FROM STDIN DELIMITER ',' CSV;" 쿼리문을 실행시켜서 CSV 파일을 Table 에 copy 해주겠습니다. 


query = """

    COPY school FROM STDIN DELIMITER ',' CSV;

"""


with open('C:/Users/admin/Documents/data/school.csv', 'r') as f:

    cur.copy_expert(query, f)

    

conn.commit()


# close connection

conn.close()

 


PGAdmin 에 들어가서 school 테이블을 조회해보니 아래처럼 데이터가 잘 copy 되서 들어가 있네요. 


Python에서 DB connect해서 데이터 조회하고 DataFrame으로 만들어서 한번 더 확인을 해보았습니다. 아래와 같이 데이터가 Postgresql DB의 school table에 잘 들어가 있음을 확인할 수 있습니다. 


# check 

cur.execute("SELECT * FROM school;")

school_df = cur.fetchall()

school_df

Out[39]: 

[('gangnam', Decimal('100'), Decimal('91')),

 ('secho', Decimal('120'), Decimal('95')),

 ('bundang', Decimal('150'), Decimal('92')),

 ('mokdong', Decimal('90'), Decimal('93'))] 



많은 도움이 되었기를 바랍니다. 



728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas 의 DataFrame에서 문자열(string)을 데이터 형태로 가지는 칼럼을 특정 기준(separator, delimiter) 분할(split a string)하여, 그 중의 일부분을 가져다가 DataFrame에 새로운 칼럼으로 만들어서 붙이는 2가지 방법을 소개하겠습니다. 

 

(1) Vectorization을 이용한 pandas DataFrame 문자열 칼럼 분할하기

(2) For Loop operation을 통한 pandas DataFrame 문자열 칼럼 분할하기

 

Python pandas DataFrame: Split string column and make a new column using part of it.

 

 

(1) Vectorization을 이용한 pandas DataFrame 문자열 칼럼 분할하기 (빠름 ^^)


예제로 사용할 문자열 'id' 와 숫자형 'val' 의 두 개 칼럼으로 이루어진 DataFrame을 만들어보겠습니다. 그리고 문자열 'id' 칼럼을 구분자(separator) '_' 를 기준으로 str.split('_') 메소드를 사용하여 분할(split) 한 후에, 앞부분([0])을 가져다가 'grp'라는 칼럼을 추가하여 만들어보겠습니다. 

 

import numpy as np
import pandas as pd

 

df = pd.DataFrame({'id': ['A_001', 'A_002', 'A_003', 'B_001', 'C_001', 'C_002'], 
                          'val': np.arange(6)})

 

print(df)

   id       val

0 A_001  0

1 A_002  1

2 A_003  2

3 B_001  3

4 C_001  4

5 C_002  5

 

# 1. vectorization
df['grp'] = df.id.str.split('_').str[0]

print(df)

   id       val  grp

0 A_001  0    A

1 A_002  1    A

2 A_003  2    A

3 B_001  3    B

4 C_001  4    C

5 C_002  5    C

 

 

만약 리스트(list)로 만들고 싶으면 분할한 객체에 대해 tolist() 메소드를 사용하면 됩니다. 

# tolist()
grp_list = df.id.str.split('_').str[0].tolist()
print(grp_list)

['A', 'A', 'A', 'B', 'C', 'C']

 

 

 

(2) For Loop operation을 통한 pandas DataFrame 문자열 칼럼 분할하기 (느림 -_-;;;)


두번째는 For Loop 연산을 사용하여 한 행, 한 행씩(row by row) 분할하고, 앞 부분 가져다가 'grp' 칼럼에 채워넣고... 를 반복하는 방법입니다. 위의 (1)번의 한꺼번에 처리하는 vectorization 대비 (2)번의 for loop은 시간이 상대적으로 많이 걸립니다. 데이터셋이 작으면 티가 잘 안나는데요, 수백~수천만건이 되는 자료에서 하면 느린 티가 많이 납니다. 

 

# 2. for loop
df = pd.DataFrame({'id': ['A_001', 'A_002', 'A_003', 'B_001', 'C_001', 'C_002'], 
                  'val': np.arange(6)})

 

for i in range(df.shape[0]):
    df.loc[i, 'grp'] = str(df.loc[i, 'id']).split('_')[0]

 

print(df)

   id       val  grp

0 A_001  0    A

1 A_002  1    A

2 A_003  2    A

3 B_001  3    B

4 C_001  4    C

5 C_002  5    C

 

많은 도움이 되었기를 바랍니다.

728x90
반응형
Posted by Rfriend
,


SAS나 SPSS를 사용하다가 R을 처음 배우는 사용자라면 R에서 7개로 나누는 데이터 구조에 대해서 '이걸 왜 배우지? SAS나 SPSS는 이런거 모르고도 아무 문제 없이 데이터 처리, 분석 다 했었는데...'라는 의문과 함께, 'R 이거 배우기 어렵네...'라고 푸념할 수도 있겠습니다. 제가 그랬거든요. (SAS나 SPSS에서 주로 사용했던 데이터 구조가 R의 데이터 구조 중에서는 '데이터 프레임' or '행렬'이라고 하는 구조라고 생각하시면 됩니다. 전부다는 아니고 많은 경우....)


R의 데이터 구조별 특성에 대해서 정확하게 이해하지 않으면 나중에 데이터 처리, 분석 넘어갔을 때 자꾸 헷갈리고, 에러가 났을 때 에러 메시지가 무슨 의미인지 이해를 못할 가능성이 높습니다. 데이터 구조에 따라서 분석기법이 달라지게 되거든요. 라틴댄스로 치자면 빨리 '패턴' 배워서 멋지게 파트너와 춤추고 싶은데 선생님은 한달이고 두달이고 '스탭'만 연습시키는데요, 어찌보면 따분하고 답답한 '스탭' 기본기가 R로 치면 데이터 구조라고 생각하시면 되겠습니다. R의 기본이 되는 중요한 개념이므로, 그리고 나중에 이게 제대로 이해가 되고 R이 손에 익었다 싶을 때 다시 되돌아 보면 R에서 데이터 구조를 이렇게 나누어서 분석 기법을 달리 하는 것이 R의 차별화된 장점이자 특징이겠구나 하고 느끼게 되는 시점이 올겁니다. 


R 데이터 구조는 (1) 스칼라, (2) 벡터, (3) 요인, (4) 행렬, (5) 배열, (6) 데이터프레임, (7) 리스트의 7개로 나눌 수 있습니다. 하나씩 설명을 할텐데요, 처음에 잘 이해가 안가도 자꾸 R 사용하면서 다시 이번 포스팅 다시 돌아와서 한번씩 복습하시면 이해되는 날이 올거예요. (제가 Coursera로 강의 듣는데 R 데이터 구조라면서 강사가 막 영어로 뭐라 뭐라 하는데.... 뭔 소리인지 이해도 안되고, 이걸 왜 배우나 싶고, 짜증도 나고, 좌절도 되고...암튼 그랬는데요, 어느 순간 지나서 보니깐 다 이해를 하고 있더라고요. 한번 보고서 이해 안된다고 좌절하지 마시라는 뜻에서 자꾸 같은 소리 하고 있습니다. ^^;;;)



1. 스칼라 (Scala)


구성인자가 하나인 벡터를 말합니다. 


> # 스칼라 (Scala) : 구성인자가 1개인 벡터

> s1 <- c(1)

> s2 <- c("Kim") 



2. 벡터 (Vector)


벡터는 동일한 유형의 데이터가 구성인자가 1개 이상이면서 1차원으로 구성되어 있는 데이터 구조를 말합니다. 

(벡터 중에서 구성인자가 1개인 것을 '스칼라'라고 합니다)


> # Vector

> v1 <- c(1, 2, 3)                   # 숫자형 벡터

> v2 <- c("Kim", "Lee", "Choi")    # 문자형 벡터

> v3 <- c(TRUE, TRUE, FALSE)   # 논리형 벡터



3. 요인 (Factor) 


범주형(명목형 또는 순서형)의 데이터 구조를 요인(Factor)라고 합니다. 통계 분석 할 때 소위 '~~별' 분석을 할 때 쓰는게 요인이므로 굉장히 많이 사용됩니다. 나중에 분석을 하다보면 (1) '요인'으로 데이터를 변환해야 하는 경우도 생기고, (2) 반대로 '요인'이 아니어야 하는데 '요인'으로 데이터가 입력이 되어있어서 에러가 발생하는 경우도 생기곤 합니다. '요인'이 뭔지, 뭐에 쓰는 것인지 모르면 두 가지 경우 상황 파악을 못해서 곤혹스럽겠지요? 

요인이 가질 수 있는 값들을 '수준(level)'이라고 합니다. RDBMS에서의 '코드값'이라고 이해하면 되겠습니다. 수준(level)은 명목형은 상관없지만, 순서형의 경우 순서(order)를 부여할 수 있습니다. 분석 결과가 순서대로 범주화 되서 나와야 보기에 좋겠지요?


> # (1) 문자형 데이터를 그냥 입력하면, 따옴표가 있는 문자형 벡터가 생성

> f1 <- c("Middle", "Low", "High")

> f1

[1] "Middle" "Low"    "High"  

>

> # (2) factor()함수를 이용해서 문자형 벡터를 요인(factor)로 변환

> # 단, 순서를 지정 안해주면 알파벳 순서로 수준(level)이 자동으로 지정됨

> f2 <- factor(f1)

> f2

[1] Middle Low    High  

Levels: High Low Middle

>

> # (3) 수준(level)에 순서를 부여하려면 'order=TRUE' 옵션 설정, level=c("") 에 순서대로 입력

> f3 <- factor(f2, order = TRUE, level = c("Low", "Middle", "High"))

> f3

[1] Middle Low    High  

Levels: Low < Middle < High 



4. 행렬 (Matrix) 


행렬은 동일한 유형의 2차원 데이터 구조를 말합니다. (쉽게 말해 m x n 형태의 표 형태의 데이터)

참고로, 벡터는 동일한 유형의 1차원 데이터 구조라고 했지요. (쉽게 말해, 가로로 늘어선 한 줄 데이터)


행렬은 matrix() 라는 함수를 사용합니다. 

최적화(optimization) 할 때 제약조건을 행렬로 입력합니다. 공학에서 행렬 많이 사용합니다. 


> # 1~12까지의 숫자를 행(row)의 수가 4개인 행렬로 만들어라

> m1 <- matrix(1:12, nrow=4)

> m1

     [,1] [,2] [,3]

[1,]    1    5    9

[2,]    2    6   10

[3,]    3    7   11

[4,]    4    8   12

> # 1~12까지의 숫자를 행(row)의 수가 4개이고 행렬로 만드는데, 행 기준(byrow=TRUE)으로 채워나가라

> m2 <- matrix(1:12, nrow=4, byrow=TRUE)

> m2

     [,1] [,2] [,3]

[1,]    1    2    3

[2,]    4    5    6

[3,]    7    8    9

[4,]   10   11   12

>  



5. 배열 (Array) 


배열(Array)은 동일한 유형의 데이터가 2차원 이상으로 구성된 구조를 말합니다. 

참고로, 행렬은 동일한 유형의 2차원 데이터 구조라고 했지요. 따라서 배열은 쉽게 말해 행렬이라는 방을 층 층이 쌓아놓은 아파트라고 생각하시면 되겠습니다. 


> # 1~24까지의 숫자를 '2 x 3 행렬'로 해서 '4층' 짜리의 데이터 구조를 만들어라

> a1 <- array(1:24, c(2,3,4))

> a1

, , 1


     [,1] [,2] [,3]

[1,]    1    3    5

[2,]    2    4    6


, , 2


     [,1] [,2] [,3]

[1,]    7    9   11

[2,]    8   10   12


, , 3


     [,1] [,2] [,3]

[1,]   13   15   17

[2,]   14   16   18


, , 4


     [,1] [,2] [,3]

[1,]   19   21   23

[2,]   20   22   24 



> # 1~24까지의 숫자를 '3 x 4' 행렬로 해서 '2층'짜리의 데이터 구조를 만들어라

> a2 <- array(1:23, c(3,4,2))

> a2

, , 1


     [,1] [,2] [,3] [,4]

[1,]    1    4    7   10

[2,]    2    5    8   11

[3,]    3    6    9   12


, , 2


     [,1] [,2] [,3] [,4]

[1,]   13   16   19   22

[2,]   14   17   20   23

[3,]   15   18   21    1





6. 데이터 프레임 (Data Frame) 


데이터 프레임데이터 유형에 상관없이 2차원 형태의 데이터 구조를 말합니다. 

참고로, 행렬동일한 유형의 데이터가 2차원 형태로 구성되었다고 했지요. 

통계, 마이닝 분석할 때 데이터 프레임을 주로 사용합니다. 


> # 다른 유형의 벡터 생성

> d1 <- c(1,2,3,4)

> d2 <- c("Kim", "Lee", "Choi", "Park")

> # 데이터 프레임으로 묶기 : data.frame() 함수 사용

> d3 <- data.frame(cust_id = d1, last_name = d2)  # 변수명 부여

> d3

  cust_id last_name

1       1       Kim

2       2       Lee

3       3      Choi

4       4      Park 



7. 리스트 (List) 


리스트는 벡터, 행렬, 배열, 데이터 프레임 등과 같은 서로 다른 구조의 데이터를 모두 묶은 객체를 말합니다. 

참고로, 리스트 말고 나머지들은 서로 다른 구조의 데이터 끼리는 묶어 놓지 않았고 따로 따로 였지요. 

R에서는 통계 분석 결과가 보통 리스트 구조로 제시되고, 필요로 하는 통계량이 있으면 indexing해서 뽑아서 쓰기도 합니다. 

서로 다른 구조의 다수의 데이터 객체를 개별로 따로 따로 관리하는 것보다는, 이것들을 리스트라는 한 바구니에 가지런히 정리해서 모아놓으면 관리하기에 편하겠지요? 


> # Vector(L1), Matrix(L2), Array(L3), Data Frame(L4)를 만들어서, 하나의 List(L5)로 묶어라

> L1 <- c(1, 2, 3, 4) # Vector

> L2 <- matrix(1:6, 3, byrow=TRUE) # Matrix

> L3 <- array(1:24, c(3,4,2)) # Array

> L4 <- data.frame(cust_id = c(1, 2, 3, 4), last_name = c("Kim", "Lee", "Choi", "Park")) # Data Frame

> L5 <- list(L1, L2, L3, L4) # List

>

> # [[1]]는 Vector(L1), [[2]]는 Matrix(L2), [[3]]는 Array(L3), [[4]]는 Data Frame(L4)가 묶인 것임

> L5

[[1]]

[1] 1 2 3 4


[[2]]

     [,1] [,2]

[1,]    1    2

[2,]    3    4

[3,]    5    6


[[3]]

, , 1


     [,1] [,2] [,3] [,4]

[1,]    1    4    7   10

[2,]    2    5    8   11

[3,]    3    6    9   12


, , 2


     [,1] [,2] [,3] [,4]

[1,]   13   16   19   22

[2,]   14   17   20   23

[3,]   15   18   21   24



[[4]]

  cust_id last_name

1       1       Kim

2       2       Lee

3       3      Choi

4       4      Park

 


지금까지 살펴본 R의 데이터 구조를 도식화하면 아래와 같습니다. 뭐가 뭐의 부분집한인지, 각 데이터 구조를 구분하는 기준은 무엇인지 유심히 다시 한번 살펴보기 정리해보면 좋겠습니다. 



[ R 데이터 구조 (Data Structure in R) ]



많은 도움 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^


728x90
반응형
Posted by Rfriend
,