이번 포스팅에서는 Python pandas의 DataFrame, Series 에서 특정 변수를 기준으로 순서를 구할 때 사용하는 rank() 함수를 소개하겠습니다. 

순위(Rank)는 정렬(Sort)와 밀접한 관련이 있는데요, 참고로 Python에서 자료형별 정렬(Sort) 방법은 아래 링크를 참고하세요. 


  (1) 다양한 동점 처리방법(tie-breaking methods)에 따른 순위(rank) 구하기 비교

순위(rank)를 구할 때 기준 변수의 점수(score)가 동일(tie)한 관측치를 어떻게 처리하는지에 따라서 5가지 방법이 있습니다. 

[ 순위 구할 때 동점 처리하는 5가지 방법 ]

  • 평균(method='average') : 동점 관측치 간의 그룹 내 평균 순위 부여 (default 설정)
  • 최소값(method='min') : 동점 관측치 그룹 내 최소 순위 부여
  • 최대값(method='max') : 동점 관측치 그룹 내 최대 순위 부여
  • 첫번째 값 (method='first') : 동점 관측치 중에서 데이터 상에서 먼저 나타나는 관측치부터 순위 부여
  • 조밀하게 (method='dense') : 최소값('min')과 같은 방법으로 순위부여하나, 'min'과는 다르게 그룹 간 순위가 '1'씩 증가함 (like ‘min’, but rank always increases by 1 between groups)


동점을 포함하고 있는 간단한 예제 DataFrame을 만들어보겠습니다. 

In [1]: import numpy as np


In [2]: import pandas as pd


In [3]: df = pd.DataFrame({

   ...: 'name': ['kim', 'lee', 'park', 'choi', 'jung', 'gang', 'nam'],

   ...: 'score': [70, 95, 100, 95, 70, 90, 70]

   ...: }, columns=['name', 'score'])


In [4]: df

Out[4]:

name score

0 kim 70

1 lee 95

2 park 100

3 choi 95

4 jung 70

5 gang 90

6 nam 70


이제 순위 구할 때 동점을 처리하는 5가지 방법별로 순위 결과가 어떻게 다른지 확인해보겠습니다. (예제를 시험점수로 가정하고, 점수가 높을 수록 상위 순위가 나오도록 함. ascending = False)

In [5]: df['rank_by_average'] = df['score'].rank(ascending=False) # rank default method='average

In [6]: df['rank_by_min'] = df['score'].rank(method='min', ascending=False)


In [7]: df['rank_by_max'] = df['score'].rank(method='max', ascending=False)


In [8]: df['rank_by_first'] = df['score'].rank(method='first', ascending=False)


In [9]: df['rank_by_dense'] = df['score'].rank(method='dense', ascending=False)


In [10]: df

Out[10]:

  name   score   rank_by_average    rank_by_min   rank_by_max    rank_by_first \

0 kim        70                   6.0              5.0              7.0              5.0

1 lee         95                   2.5              2.0              3.0              2.0

2 park      100                  1.0               1.0              1.0              1.0

3 choi        95                  2.5               2.0              3.0              3.0

4 jung        70                  6.0               5.0              7.0              6.0

5 gang       90                  4.0               4.0              4.0              4.0

6 nam        70                  6.0               5.0              7.0              7.0


rank_by_dense

0            4.0

1            2.0

2            1.0

3            2.0

4            4.0

5            3.0

6            4.0



  (2) 순위를 오름차순으로 구하기 (Rank in Ascending order)

rank(ascending = True) 으로 설정해주면 오름차순 (제일 작은 점수가 순위 '1'이고, 점수가 높아질수록 하나씩 순위 증가)으로 순위를 구합니다. Default 설정이 ascending=True 이므로 별도로 설정을 안해줘도 자동으로 오름차순 순위가 구해집니다. 

In [11]: df_score = df[['name', 'score']].copy()


In [12]: df_score['rank_ascending'] = df_score['score'].rank(method='min', ascending=True)


In [13]: df_score

Out[13]:

name    score     rank_ascending

0 kim        70                  1.0

1 lee         95                    5.0

2 park      100                    7.0

3 choi        95                    5.0

4 jung        70                    1.0

5 gang       90                    4.0

6 nam        70                    1.0 



  (3) 그룹 별로 순위 구하기 (Rank by Groups): df.groupby().rank()

Groupby operator를 사용하면 그룹별로 따로 따로 순위를 구할 수 있습니다. 

In [14]: from itertools import chain, repeat

    ...:


In [15]: df2 = pd.DataFrame({

    ...: 'name': ['kim', 'lee', 'park', 'choi']*3,

    ...: 'course': list(chain.from_iterable((repeat(course, 4)

    ...: for course in ['korean', 'english', 'math']))),

    ...: 'score': [70, 95, 100, 95, 65, 80, 95, 90, 100, 85, 90, 90]

    ...: }, columns=['name', 'course', 'score'])


In [16]: df2

Out[16]:

    name   course  score

0    kim   korean     70

1    lee   korean     95

2   park   korean    100

3   choi   korean     95

4    kim  english     65

5    lee  english     80

6   park  english     95

7   choi  english     90

8    kim     math    100

9    lee     math     85

10  park     math     90

11  choi     math     90


In [17]: df2['rank_by_min_per_course'] = df2.groupby('course')['score'].rank(method='min', ascending=False)


In [18]: df2

Out[18]:

    name   course  score  rank_by_min_per_course

0    kim   korean     70                     4.0

1    lee   korean      95                     2.0

2   park   korean    100                     1.0

3   choi   korean      95                     2.0

4    kim  english      65                     4.0

5    lee  english       80                     3.0

6   park  english      95                     1.0

7   choi  english      90                     2.0

8    kim     math    100                     1.0

9    lee     math      85                     4.0

10  park     math     90                     2.0

11  choi     math     90                     2.0



  (4) 칼럼을 기준으로 순위 구하기 (Rank over the columns): df.rank(axis=1)

위의 (1), (2), (3) 번의 예시는 전부 행을 기준(위/아래 방향)으로 한 순위(rank over the rows) 였습니다. 필요에 따라서는 열을 기준(왼쪽/오른쪽 방향)으로 한 순위(rank over the columns)을 해야할 때도 있을텐데요, rank(axis=1) 을 설정해주면 열 기준 순위를 구할 수 있습니다. 

In [19]: df3 = pd.DataFrame({

    ...: 'col_1': [1, 2, 3, 4],

    ...: 'col_2': [3, 5, 1, 2],

    ...: 'col_3': [3, 1, 2, 4]})


In [20]: df3

Out[20]:

 col_1 col_2 col_3

0   1      3      3

1   2      5      1

2   3      1      2

3   4      2      4


In [21]: df3.rank(method='min', ascending=False, axis=1)

Out[21]:

   col_1    col_2    col_3

0    3.0       1.0       1.0

1    2.0       1.0       3.0

2    1.0       3.0       2.0

3    1.0       3.0       1.0 


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

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


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
,

이번 포스팅에서는 빠르고 메모리를 효율적으로 사용해서 반복자((fast and memory-efficient iterator)를 만들어주는 itertools 모듈을 사용해서 리스트 원소를 n번 반복하고 묶어서 새로운 리스트를 만드는 4가지 유형의 방법을 소개하겠습니다. 


먼저 itertools 모듈에서 chain(), repeat() 함수를 불러오고, 예제로 사용할 반복할 리스트(numbers)와 반복할 회수(n)을 만들어놓겠습니다. 

 In [1]: from itertools import chain, repeat

In [2]: numbers = [1, 2, 3]


In [3]: n = 3


  (1) itertools.repeat(object, times) : object를 times 만큼 반복하기


itertools의 repeat() 함수로 [1, 2, 3] 리스트를 3번 반복하면, 아래처럼 리스트 안에 [1, 2, 3] 리스트가 각 각 분리([1, 2, 3], [1, 2, 3], [1, 2, 3])되어서 들어가 있습니다. 

 

In [4]: list(repeat(numbers, n))

Out[4]: [[1, 2, 3], [1, 2, 3], [1, 2, 3]]




  (2) itertools.chain.from_iterable(repeat(object, times))) 
      : times 만큼 "object 전체"를 반복한 연속된 서열을 하나의 서열로 묶어줌


위의 (1)번과는 다르게, [1, 2, 3, 1, 2, 3, 1, 2, 3] 의 단 하나의 리스트로 묶였습니다. 


In [5]: list(chain.from_iterable(repeat(numbers, n)))

Out[5]: [1, 2, 3, 1, 2, 3, 1, 2, 3]

 



  (3) itertools.chain.from_iterable((repeat(object, times) for object in objects)))
       : "objects 내 각 원소"를 times만큼 반복한 연속된 서열을 하나의 서열로 묶어줌

list comprehension 으로 for loop을 이용하여서 numbers 리스트 안의 각 원소인 1, 2, 3 별로 3번씩 반복한 후, 이를 하나의 리스트로 묶어준 경우입니다. 


In [6]: list(chain.from_iterable((repeat(number, n) for number in numbers)))

Out[6]: [1, 1, 1, 2, 2, 2, 3, 3, 3]

 



  (4) itertools.chain.from_iterable((repeat(object, time)
                                              for (object, time) in zip(objects, times))))

  : objects 내 각 원소 다른 수의 times 만큼 반복한 연속된 서열을 하나의 서열로 묶어줌

반복할 수 times 인자를 반복할 대상 objects 내 원소별로 다르게 하고 싶을 때가 있습니다. 이럴 때는 zip() 으로 반복할 대상 object와 반복할 회수 time을 짝으로 묶어서 반복을 시켜주면 됩니다. 


In [7]: numbers = [1, 2, 3]

   ...: n_list = [3, 5, 7]


In [8]: list(chain.from_iterable((repeat(number, n) for (number, n) in zip(numbers, n_list))))

Out[8]: [1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3] 



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

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


728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas DataFrame의 index를 reset_index() 하여 칼럼으로 가져오고, 이렇게 가져온 index에 새로운 이름을 부여하는 3가지 방법을 소개하겠습니다. 





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



import numpy as np

import pandas as pd


df = pd.DataFrame(np.arange(10).reshape(5, 2), 

                 columns=['x1', 'x2'], 

                 index=['a', 'b', 'c', 'd', 'e'])


df


x1x2
a01
b23
c45
d67
e89

 




이제 index 를 칼럼으로 가져오고, 가져온 index의 이름으로 'id'라는 이름을 부여하는 3가지 방법을 차례대로 소개하겠습니다. 


  (1) reset_index() 한 후에 rename()으로 새로운 이름 부여하기



# (1) reset_index() and rename

df.reset_index().rename(columns={"index": "id"})

 

idx1x2
0a01
1b23
2c45
3d67
4e89





  (2) rename_axis() 로 index의 이름을 먼저 바꾸고, 이후에 reset_index() 하기



# (2) rename_axis() first, reset_index() second

df_1 = df.rename_axis('id').reset_index()

df_1

 

idx1x2
0a01
1b23
2c45
3d67
4e89





  (3) df.index.name 으로 index에 이름 할당하고, 다음으로 reset_index() 하기



# (3) assing index name and reset_index()

df.index.name = 'id'

df_2 = df.reset_index()

df_2

 

idx1x2
0a01
1b23
2c45
3d67
4e89



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



728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas의 DataFrame을 


(1) 특정 칼럼을 기준으로 행을 정렬한 후에 (sort DataFrame by value in ascending/descending order)

==> (2) 각 그룹별로 상위 N개 행을 가져오기 (select top N rows by group)


을 하는 방법을 소개하겠습니다. 





먼저 'a'와 'b' 두 개의 그룹별로 5개의 값을 가진 간단한 예제 DataFrame을 만들어보겠습니다. 



import numpy as np

import pandas as pd


# make a sample DataFrame

df = pd.DataFrame({'grp': ["a", "a", "a", "a", "a", "b", "b", "b", "b", "b"], 

                           'val': np.random.uniform(0, 10, 10)})


df


grpval
0a0.275704
1a5.334576
2a5.386807
3a6.033636
4a2.140798
5b2.089792
6b6.396985
7b3.088498
8b5.895689
9b1.157073

 




이제 "val" 변수를 기준으로 내림차순 정렬(sort by 'val' in descending order) 한 후에, 'grp' 칼럼의 'a', 'b' 그룹별로 상위 3개의 값을 가져와서 새로운 데이터프레임을 만들어보겠습니다. 



# sort by value in descending order per group, and select top 3 values per group

df_sort_group_top3 = df.sort_values(by="val", ascending=False).groupby("grp").head(3)


df_sort_group_top3


grpval
6b6.396985
3a6.033636
8b5.895689
2a5.386807
1a5.334576
7b3.088498

 



위의 df_sort_group_top3 결과를 좀더 보기에 좋도록 'a', 'b' 그룹 순서대로, 각 그룹 내에서는 내림차순으로 정렬해보겠습니다. 



df_sort_group_top3.sort_values(by=["grp", "val"], ascending=[True, False])


grpval
3a6.033636
2a5.386807
1a5.334576
6b6.396985
8b5.895689
7b3.088498

 




사용자 정의함수를 작성하고 df.groupby("grp").apply(UDF) 를 사용하는 방법도 있습니다. apply(UDF_name, arguments) 형식으로 사용자 정의 함수에서 사용했던 매개변수를 같이 넣어주면 됩니다. 매개변수 값을 변경해서 여러번 사용해야 하는 경우에는 아래처럼 사용자 정의 함수를 사용하는게 아무래도 편리하고 코드도 깔끔하겠습니다. 

(물론, 아래의 예의 경우 사용자 정의함수에서 default 값으로 입력해놓은 매개변수 값과 동일하기 때문에 apply(top) 만 해도 결과는 동일합니다.) 



def top(df, n=3, column='val'):

    return df.sort_values(by="val", ascending=False)[:n]

 

df.groupby("grp").apply(top, column="val", n=3)

grpval
grp
a2a8.707697
3a8.288310
0a5.317945
b9b9.460717
5b7.317662
8b6.277714





정렬하는 칼럼이나 Top N개가 고정되어 있거나 일회성인 경우에는 간단하게 lambda 를 사용해서 사용자 정의 함수를 정의해서 사용해도 되겠습니다. 



top2 = lambda x: x.sort_values(by='val', ascending=False)[:3]


df.groupby('grp').apply(top2)

grpval
grp
a2a8.707697
3a8.288310
0a5.317945
b9b9.460717
5b7.317662
8b6.277714

 



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



728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas의 pivot_table() 함수를 사용할 때 


- (1) 'DataError: No numeric types to aggregate' 에러가 왜 생기는지

- (2) 'DataError: No numeric types to aggregate' 에러 대응방법은 무엇인지에 대해서 알아보겠습니다. 



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



In [1]: import numpy as np

   ...: import pandas as pd


In [2]: df = pd.DataFrame({'id': [1, 1, 2, 2, 3, 3], 

   ...:                    'col': ['x1', 'x2', 'x1', 'x2', 'x1', 'x2'], 

   ...:                    'sum': [30, 10, 70, 40, 20, 80], 

   ...:                    'name': ['a', 'a', 'b', 'b', 'c', 'c']})

   ...: 

   ...: df

Out[2]: 

  col  id name  sum

0  x1   1    a   30

1  x2   1    a   10

2  x1   2    b   70

3  x2   2    b   40

4  x1   3    c   20

5  x2   3    c   80

 



이제 pandas의 pivot_table() 함수를 이용해서 'id'변수를 index로, 'col' 변수를 열(column)로 하여 'sum'이라는 숫자형 데이터 값(values)을 재구조화(pivot) 해보겠습니다.  pivot_table() 함수의 집계함수(aggregation function)의 디폴트 설정은 평균(aggfunc='mean')으로 되어 있습니다. 아래 코드는 문제 없이 잘 수행이 되었습니다. 



In [3]: df.pivot_table(index = 'id', columns='col', values=['sum'])

Out[3]: 

    sum    

col  x1  x2

id         

1    30  10

2    70  40

3    20  80


In [4]: df.pivot_table(index = 'id', columns='col', values=['sum'], aggfunc='mean')

   ...: 

Out[4]: 

    sum    

col  x1  x2

id         

1    30  10

2    70  40

3    20  80

 



 

 (1) 'DataError: No numeric types to aggregate' 에러가 왜 생기는가?


이번에는 'id' 변수를 index로, 'col'변수를 열(column)변수로 하는 것은 위와 동일하나, 재구조화하는 테이블의 값(value)으로 숫자형(nemeric)이 아니라 문자형(character)인 'name' 변수를 사용해보겠습니다. 그러면 아래와 같이 'DataError: No numeric types to aggregate'라는 DataError가 납니다. 왜냐하면 값(values) 으로 사용하려는 'name' 변수가 집계가 불가능한 문자형 데이터이기 때문입니다. (numeric only)



In [5]: df.pivot_table(index = 'id', columns='col', values=['name']) # default aggfunc='mean'

Traceback (most recent call last):


  File "<ipython-input-5-9a2cccdff2ef>", line 1, in <module>

    df.pivot_table(index = 'id', columns='col', values=['name'])


  File "C:\Users\admin\Anaconda3\lib\site-packages\pandas\core\groupby.py", line 3048, in _cython_agg_general

    how, numeric_only=numeric_only)


    .... 중간 생략 ....


DataError: No numeric types to aggregate

 




 (2) 'DataError: No numeric types to aggregate' 에러 대응방법은?


집계함수를 aggfunc='first' 로 명시적으로 설정해 줌으로써, 디폴트인 'mean' 을 사용해서 집계하는 것이 아니라 재구조화하는 기준의 테이블의 각 cell의 첫번째 값('first')을 그냥 가져오게끔 해주면 됩니다. 



In [6]: df.pivot_table(index = 'id', columns='col', values=['name'], aggfunc='first')

Out[6]: 

    name   

col   x1 x2

id         

1      a  a

2      b  b

3      c  c

 



집계함수 aggfunc='first' 로 해서 pivot 한 테이블의 값(values)을 하나가 아니라 여러개로 할 수도 있습니다. (이렇게 하면 숫자형 변수 'sum'도 집계를 하는 것이 아니라 각 테이블 cell의 첫번째 값을 가져오게 됨)



In [7]: df.pivot_table(index = 'id', columns='col', values=['name', 'sum'], aggfunc='first')

Out[7]: 

    name    sum    

col   x1 x2  x1  x2

id                 

1      a  a  30  10

2      b  b  70  40

3      c  c  20  8

 



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

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas의 DataFrame, Series 의 행, 열의 개수를 세는 방법(how to count the number of rows and columns of pandas DataFrame and Series)을 소개하겠습니다. 

 

간단한 것들이고, 이미 소개한 것들이긴 한데요, Stackoverflow에 깔끔하게 유형별로 정리한 표가 있어서 옮겨보았습니다. 

 

Series 의 행 개수를 셀 때 s.size 와 같이 뒤에 () 가 없다는 것 조심해야 겠습니다.그리고 count()는 Null 값이 아닌 행(count Non-null rows)만 세며,

> size() 는 Null 값인 행도 모두 포함해서 행(size of all rows)을 센다

는 것도 유념하면 좋겠습니다. 

 

구분

pandas DataFrame (df)

pandas Series (s)

행 개수 세기

(row count)

len(df)

df.shape[0]

len(df.index)

len(s)

s.size

len(s.index)

열 개수 세기

(column count)

df.shape[1]

len(df.columns)

N/A

Null 값이 아닌 행 개수 세기

(Non-null row count)

df.count()

s.count()

그룹 별 행 개수 세기

(Row count per group)

df.groupby(...).size()

s.groupby(...).size()

그룹 별 Null 값이 아닌 행 개수 세기

(Non-null row count per group)

df.groupby(...).count()

s.groupby(...).count()

 

 

간단한 예제를 아래에 소개합니다. 

numpy와 pandas 라이브러리 불러오고, DataFrame과 Series 데이터셋 만들어보겠습니다. 

 

import numpy as np
import pandas as pd

df = pd.DataFrame({'grp': ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C'], 
                           'val': [1, 2, np.nan, 4, np.nannp.nan, 7, 8, 9]})

In [02]: df

Out[02]:

grp val

0 A 1.0

1 A 2.0

2 A NaN

3 B 4.0

4 B NaN

5 B NaN

6 C 7.0

7 C 8.0

8 C 9.0

 


s = pd.Series([1, 2, np.nan, 4, np.nannp.nan, 7, 8, 9])
In [03]: s

Out[03]:

0 1.0

1 2.0

2 NaN

3 4.0

4 NaN

5 NaN

6 7.0

7 8.0

8 9.0

dtype: float64

 


 

구분

DataFrame (df)

Series (s)

행의 개수 세기

(Row count)

In [04]: len(df)

Out[04]: 9

 

In [05]: df.shape[0]

Out[05]: 9

 

In [06]: len(df.index)

Out[06]: 9

In [08]: len(s)

Out[08]: 9

 

In [09]: s.size

Out[09]: 9

 

In [10]: len(s.index)

Out[10]: 9

열의 개수 세기

(Column count)

In [11]: df.shape[1]

Out[11]: 2

 

In [12]: len(df.columns)

Out[12]: 2

N/A

Null 이 아닌 행의 개수 세기

(Non-null row count, ignore NaNs)

In [13]: df.count()

Out[13]:

grp 9

val 6

dtype: int64

 

In [14]: df['val'].count()

Out[14]: 6

In [15]: s.count()

Out[15]: 6

그룹 별 행의 개수 세기

(Row count per group)

In [16]: df.groupby('grp').size()

Out[16]:

grp

A 3

B 3

C 3

dtype: int64

In [17]: s.groupby(df.grp).size()

Out[17]:

grp

A 3

B 3

C 3

dtype: int64

그룹별 Null 이 아닌 행의 개수 세기

(Non-null row count per group)

In [18]: df.groupby('grp').count()

Out[18]:

val

grp

A 2

B 1

C 3

In [19]: s.groupby(df.grp).count()

Out[19]:

grp

A 2

B 1

C 3

dtype: int64

 

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

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
,

이번 포스팅에서는 (1) text 또는 csv 포맷으로 저장된 텍스트 파일을 Python의 string methods 를 사용하여 파일을 열어서 파싱하여 matrix 로 저장하고,  (2) 숫자형 데이터를 표준화(standardization) 혹은 정규화(normalization) 하는 사용자 정의함수를 만들어보겠습니다. 

 

예제로 사용할 text 파일은 전복의 성별과 length, diameter, height, whole_weight, shucked_weight, viscera_weight, shell_weight, rings 를 측정한 abalone.txt 파일 입니다. 

abalone.txt
0.18MB

1. text 파일을 읽어서 숫자형 값으로 만든 matrix, 라벨을 저장한 vector를 만들기

물론, Pandas 모듈의 read_csv() 함수를 이용하여 편리하게 text, csv 포맷의 파일을 읽어올 수 있습니다. 

# importing modules
import numpy as np
import pandas as pd
import os

# setting directory
base_dir = '/Users/ihongdon/Documents/Python'
work_dir = 'dataset'
path = os.path.join(base_dir, work_dir)

# reading text file using pandas read_csv() function
df = pd.read_csv(os.path.join(path, 'abalone.txt'), 
                 sep=',', 
                 names=['sex', 'length', 'diameter', 'height', 'whole_weight', 
                        'shucked_weight', 'viscera_weight', 'shell_weight', 'rings'], 
                 header=None)
                 
# check first 5 lines
df.head()
sex	length	diameter	height	whole_weight	shucked_weight	viscera_weight	shell_weight	rings
0	M	0.455	0.365	0.095	0.5140	0.2245	0.1010	0.150	15
1	M	0.350	0.265	0.090	0.2255	0.0995	0.0485	0.070	7
2	F	0.530	0.420	0.135	0.6770	0.2565	0.1415	0.210	9
3	M	0.440	0.365	0.125	0.5160	0.2155	0.1140	0.155	10
4	I	0.330	0.255	0.080	0.2050	0.0895	0.0395	0.055	7

 

 

위의 Pandas 의 함수 말고, 아래에는 Python의 string methods 를 사용해서 파일을 열고, 파싱하는 간단한 사용자 정의함수를 직접 만들어보았습니다.

위의 abalone.txt 파일의 데이터 형태를 참고해서 파일 이름, 숫자형 변수의 개수, 숫자형 변수의 시작 위치, 숫자형 변수의 끝나는 위치, 라벨 변수의 우치를 인자로 받는 사용자 정의함수를 정의하였습니다. 분석을 하려는 각 데이터셋에 맞게 인자와 함수 code block 을 수정하면 좀더 유연하고 데이터 특성에 적합하게 파일을 불어올 수 있는 사용자 정의함수를 만들 수 있습니다. 

def file2matrix(filename, val_col_num, val_col_st_idx, val_col_end_idx, label_idx):
    """
    - filename: directory and file name
    - val_col_num: the number of columns which contains numeric values
    - val_col_st_idx: the index of starting column which contains numeric values
    - val_col_end_idx: the index of ending column which contains numeric values
    - label_idx: the index of label column
    """
    # open file
    file_opened = open(filename)
    lines_num = len(file_opened.readlines())
    
    # blank matrix and vector to store
    matrix_value = np.zeros((lines_num, val_col_num))
    vector_label = []
    
    # splits and appends value and label using for loop statement
    file_opened = open(filename)
    idx = 0
    for line in file_opened.readlines():
        # removes all whitespace in string
        line = line.strip()
        
        # splits string according to delimiter str
        list_from_line = line.split(sep=',')
        
        # appends value to matrix and label to vector
        matrix_value[idx, :] = list_from_line[val_col_st_idx : (val_col_end_idx+1)]
        vector_label.append(list_from_line[label_idx])
        idx += 1
        
    return matrix_value, vector_label

 

Python의 문자열 메소드 (string methods)는 https://rfriend.tistory.com/327 를 참고하세요. 

 

위의 file2matrix() 사용자 정의 함수를 사용하여 abalone.txt 파일을 읽어와서 (a) matrix_value, (b) vector_label 을 반환하여 보겠습니다. 

# run file2matrix() UDF
matrix_value, vector_label = file2matrix(os.path.join(path, 'abalone.txt'), 8, 1, 8, 0)

#--- matrix_value
# type
type(matrix_value)
numpy.ndarray

# shape
matrix_value.shape
(4177, 8)

# samples
matrix_value[:3]
array([[ 0.455 ,  0.365 ,  0.095 ,  0.514 ,  0.2245,  0.101 ,  0.15  , 15.    ],
       [ 0.35  ,  0.265 ,  0.09  ,  0.2255,  0.0995,  0.0485,  0.07  ,  7.    ],
       [ 0.53  ,  0.42  ,  0.135 ,  0.677 ,  0.2565,  0.1415,  0.21  ,  9.    ]])
       
#--- vector_label
# type
type(vector_label)
list

# number of labels
len(vector_label)
4177

# samples
vector_label[:3]
['M', 'M', 'F']

 

 

2-1. 숫자형 데이터를 표준화(Standardization) 하기

위의 숫자형 데이터로 이루어진 matrix_value 를 numpy를 이용해서 표준화, 정규화하는 사용자 정의함수를 작성해보겠습니다. (물론 scipy.stats 의 zscore() 나 sklearn.preprocessing 의 StandardScaler() 함수를 사용해도 됩니다.)

 

아래의 사용자 정의 함수는 숫자형 데이터로 이루어진 데이터셋을 인자로 받으면, 평균(mean)과 표준편차(standard deviation)를 구하고, standardized_value = (x - mean) / standard_deviation 으로 표준화를 합니다. 그리고 표준화한 matrix, 각 칼럼별 평균과 표준편차를 반환합니다. 

def standardize(numeric_dataset):

    # standardized_value = (x - mean)/ standard_deviation
    
    # calculate mean and standard deviation per numeric columns
    mean_val = numeric_dataset.mean(axis=0)
    std_dev_val = numeric_dataset.std(axis=0)
    
    # standardization
    matrix_standardized = (numeric_dataset - mean_val)/ std_dev_val
    
    return matrix_standardized, mean_val, std_dev_val

 

위의 standardize() 함수를 사용하여 matrix_value 다차원배열을 표준화해보겠습니다. 

# rund standardize() UDF
matrix_standardized, mean_val, std_dev_val = standardize(matrix_value)

# matrix after standardization
matrix_standardized
array([[-0.57455813, -0.43214879, -1.06442415, ..., -0.72621157,
        -0.63821689,  1.57154357],
       [-1.44898585, -1.439929  , -1.18397831, ..., -1.20522124,
        -1.21298732, -0.91001299],
       [ 0.05003309,  0.12213032, -0.10799087, ..., -0.35668983,
        -0.20713907, -0.28962385],
       ...,
       [ 0.6329849 ,  0.67640943,  1.56576738, ...,  0.97541324,
         0.49695471, -0.28962385],
       [ 0.84118198,  0.77718745,  0.25067161, ...,  0.73362741,
         0.41073914,  0.02057072],
       [ 1.54905203,  1.48263359,  1.32665906, ...,  1.78744868,
         1.84048058,  0.64095986]])
 
 # mean per columns
 mean_val
 array([0.5239921 , 0.40788125, 0.1395164 , 0.82874216, 0.35936749,
       0.18059361, 0.23883086, 9.93368446])
       
 # standard deviation per columns
 std_dev_val
 array([0.12007854, 0.09922799, 0.04182205, 0.49033031, 0.22193638,
       0.10960113, 0.13918601, 3.22378307])

 

2-2. 숫자형 데이터를 정규화(Normalization) 하기

다음으로 척도, 범위가 다른 숫자형 데이터를 [0, 1] 사이의 값으로 변환하는 정규화(Normalization)를 해보겠습니다. normalized_value = (x - minimum_value) / (maximum_value - minimum_value) 로 계산합니다. 

def normalize(numeric_dataset):
    
    # normalized_value = (x - minimum_value) / (maximum_value - minimum_value)
    
    # calculate mean and standard deviation per numeric columns
    min_val = numeric_dataset.min(axis=0)
    max_val = numeric_dataset.max(axis=0)
    ranges = max_val - min_val
    
    # normalization, min_max_scaling
    matrix_normalized = (numeric_dataset - min_val)/ ranges
    
    return matrix_normalized, ranges, min_val

 

위의 normalize() 사용자 정의 함수에 matrix_value 다차원배열을 적용해서 정규화 변환을 해보겠습니다. 정규화된 다차원배열과 범위(range = max_val - min_val), 최소값을 동시에 반환합니다. 

# run normalize() UDF
matrix_normalized, ranges, min_val = normalize(matrix_value)

# normalized matrix
matrix_normalized
array([[0.51351351, 0.5210084 , 0.0840708 , ..., 0.1323239 , 0.14798206,
        0.5       ],
       [0.37162162, 0.35294118, 0.07964602, ..., 0.06319947, 0.06826109,
        0.21428571],
       [0.61486486, 0.61344538, 0.11946903, ..., 0.18564845, 0.2077728 ,
        0.28571429],
       ...,
       [0.70945946, 0.70588235, 0.18141593, ..., 0.37788018, 0.30543099,
        0.28571429],
       [0.74324324, 0.72268908, 0.13274336, ..., 0.34298881, 0.29347285,
        0.32142857],
       [0.85810811, 0.84033613, 0.17256637, ..., 0.49506254, 0.49177877,
        0.39285714]])
        
# ranges
ranges
array([ 0.74  ,  0.595 ,  1.13  ,  2.8235,  1.487 ,  0.7595,  1.0035,  28.    ])

# minimum value
min_val
array([7.5e-02, 5.5e-02, 0.0e+00, 2.0e-03, 1.0e-03, 5.0e-04, 1.5e-03, 1.0e+00])

 

다음번 포스팅에서는 텍스트 파일을 파싱해서 One-Hot Encoding 하는 방법을 소개하겠습니다. 

 

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

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 os 라이브러리를 이용한 경로 및 폴더 관리, shutil 라이브러리를 이용한 파일 복사 방법에 대한 소소한 팁들을 소개하겠습니다. 

os 라이브러리에 대해서 소개해 놓은 페이지 ( https://docs.python.org/3/library/os.html )에 가보면 '기타 운영 체계에 대한 인터페이스 (Miscellaneous operating system interfaces)' 라고 소개를 하면서 스크롤 압박에 굉장히 심할 정도로 여러개의 함수들을 소개해 놓았습니다. 

그 많은 것을 모두 소개하기는 힘들구요, 그중에서도 이번 포스팅에서는 제가 자주 쓰는 함수들만 몇 개 선별해서 소개하도록 하겠습니다. 


  1. os 라이브러리를 이용한 경로 및 폴더 생성, 조회, 변경


먼저 os 라이브러리를 불러오겠습니다. 


import os # Miscellaneous operating system interfaces



1-1. 현재 작업경로 확인하기: os.getcwd()


# os.getcwd(): returns the current working directory

os.getcwd()

'C:\\Users\\admin\\python'

 



1-2. 작업경로 안에 들어있는 파일 리스트 확인하기: os.listdir(path)


# os.listdir(path): return a list of then entries in the directory given by path

os.listdir(os.getcwd()) # a list of files at current directory

['.ipynb_checkpoints', 'numpy_adding_new_axis.ipynb', 'Numpy_clip.ipynb', 'python_os.ipynb'] 




1-3. 작업경로 바꾸기: os.chdir(path)


# os.chdir(path): change the current working directory to path

base_dir = 'C:/Users/admin'

os.chdir(base_dir)

os.getcwd()

 'C:\\Users\\admin'




1-4. 기존 경로와 새로운 폴더 이름을 합쳐서 하위 경로 만들기: os.path.join()


# join one or more path components

path = os.path.join(base_dir, 'os')

path

'C:/Users/admin\\os'




1-5. 새로운 폴더를 만들기: os.mkdir(path)

 

# create a directory named path with numeric mode

os.mkdir(path)




1-6. 경로가 존재하는지 확인하기: os.path.isdir(path)


# return True if path is an existing directory

os.path.isdir(path)

True

 



1-7. 파일이나 경로 이름 바꾸기: os.rename(old_path_name, new_path_name)


# rename the file or directory src to dst

# os.rename(src, dst)

dst_path = os.path.join(base_dir, 'os_renamed')

os.rename(path, dst_path)

os.path.isdir(dst_path) # check whether dst_path is renamed or not

True

 



  2. shutil 라이브러리를 이용한 파일 복사: shutil.copyfile(src, dst)


먼저, 파일을 복사해올 소스 경로(source directory, from)와 파일을 복사해놓은 종착지 경로(destination directory, to)를 만들어보겠습니다. 


# creating src_dir, dst_dir

base_dir = 'C:/Users/admin'

src_dir = os.path.join(base_dir, 'src_dir')

dst_dir = os.path.join(base_dir, 'dst_dir')


os.mkdir(src_dir)

os.mkdir(dst_dir)

 


다음으로, 소스 경로(src_dir)에 'file_1.txt', 'file_2.txt', 'file_3.txt' 라는 이름으로 메모장으로 작성한 간단한 텍스트 파일 3개를 저장해두었습니다. (직접 수작업으로 메모장 열고 문자 몇개 입력하고 저장함)

os.listdir() 를 사용하여 소스 경로(src_dir)에 들어있는 3개의 텍스트파일 이름을 fnames 라는 이름의 리스트로 만들어두었습니다. 


# put file_1, file_2, file_3 into src_dir

fnames = os.listdir(src_dir)

fnames

['file_1.txt', 'file_2.txt', 'file_3.txt'] 



마지막으로, shutil 라이브러리를 불러오고, shutil.copyfile(src, dst) 함수를 사용하여 소스 경로(source directory)에 들어있는 3개의 텍스트 파일을 종착지 경로(destination directory)로 복사해보겠습니다. 

이때 for loop 문을 사용하여 텍스트 파일 별로 shutil.copyfile(src, dst)를 적용해주면 됩니다. 


# copy files from src to dst directory

import shutil


for fname in fnames:

    src = os.path.join(src_dir, fname)

    dst = os.path.join(dst_dir, fname)

    shutil.copyfile(src, dst)


os.listdir(dst_dir)

['file_1.txt', 'file_2.txt', 'file_3.txt'] 




  3. os와 shutil 라이브러리를 이용한 폴더 삭제, 파일 삭제하기


아래와 같이 3개의 텍스트 파일이 들어있는 'C:/Users/admin/os' 라는 경로의 폴더를 예로 들어보겠습니다. 


os.listdir('C:/Users/admin/os')

['big_data.txt', 'my_data.txt', 'sample_data.txt']



3-1. 경로(폴더) 제거하기: os.rmdir(path)

경로(폴더) 안에 파일이 없어야지 os.rmdir()을 사용할 수 있습니다. 경로(폴더) 안에 파일이 있으면 아래처럼 "OSError: [WinError 145] 디렉토리가 비어 있지 않습니다"라는 에러가 발생합니다. 


# OSError: directory is not empty 

os.rmdir('C:/Users/admin/os')

---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
<ipython-input-11-4b25f55d427c> in <module>()
----> 1 os.rmdir('C:/Users/admin/os')

OSError: [WinError 145] 디렉터리가 비어 있지 않습니다: 'C:/Users/admin/os'


os.path.isdir(dst_path) # check whether dst_path is removed or not

False

 


3-2. 파일 삭제하기 : os.remove(path)

os.remove() 는 인자로 1개의 파일 경로를 받습니다. 한번에 한개씩 지워야 하므로 불편한점이 있습니다. 


 # delete file

os.remove('C:/Users/admin/os/my_data.txt')

os.remove('C:/Users/admin/os/big_data.txt')

os.remove('C:/Users/admin/os/sample_data.txt')



위에서 'C:/Users/admin/os' 경로 안의 파일 3개를 모두 삭제했으므로 이제 os.rmdir() 을 사용해서 폴더를 삭제할 수 있습니다. 

# delete directory only when it is empty

os.rmdir('C:/Users/admin/os') 


경로(폴더)가 존재하는지 os.path.isdir(path)로 확인해보겠습니다. 방금전에 경로를 os.rmdir()로 삭제를 했기 때문에 False 를 반환하였습니다. 

# check whether the directory is present or not

os.path.isdir('C:/Users/admin/os') 

False


3-3. 경로(폴더)와 파일을 한꺼번에 모두 삭제하기 : shutil.rmtree(path)


os.mkdir('C:/Users/admin/os')


# delete directory and files at once

import shutil

shutil.rmtree('C:/Users/admin/os')

 


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

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

728x90
반응형
Posted by Rfriend
,