Pandas

Python Pandas 날짜 데이터 다루기

개요

  • 연도, 월, 주만 있는 컬럼을 날짜 데이터 타입으로 변경하려면 어떻게 해야할까?
  • 약간의 트릭이 필요하다

데이터 생성

  • 가상의 데이터를 생성한다.
import pandas as pd

data = [
    {"year": 2020, "month": 1, "week": 2, "company" : "A회사", "revenue_pct" : 49},
    {"year": 2020, "month": 1, "week": 2, "company" : "B회사", "revenue_pct" : 51},
    {"year": 2021, "month": 1, "week": 2, "company" : "A회사", "revenue_pct" : 37},
    {"year": 2021, "month": 1, "week": 2, "company" : "B회사", "revenue_pct" : 63},
    {"year": 2022, "month": 12, "week": 1, "company" : "A회사", "revenue_pct" : 70},
    {"year": 2022, "month": 12, "week": 1, "company" : "B회사", "revenue_pct" : 30},
]

df = pd.DataFrame(data)
df

Untitled

ChatGPT 방식

  • chatGPT에서 알려준 방식으로 진행해본다.
df["date"] = pd.to_datetime(df["year"].astype(str) + df["week"].astype(str) + '1', format="%Y%W%w")
df

Untitled

  • 기대했던 것은 2022-12-1 일 방식인데, 2022-01-03이다.
  • 만약, 전체데이터가 있다면, date 날짜가 중복될 수 있다.

수정된 방식

  • 아래와 같이 수정하도록 한다.
    • 두번째 라인 코드 7-6 은 일종의 특정 일자를 지정하는 것이다.
df["date"] = pd.to_datetime(df["year"].astype(str) + "-" + df["month"].astype(str))
df["date"] = df["date"] + pd.to_timedelta(df["week"] * 7 - 6, unit="D")
df

Untitled

Verifying Outlier Values

이상값의 정의

  • 다소 주관적이며(Somewhat Subjective), 특정 분포의 중심경향성, 퍼진 정도와 형태에 따라 밀접한 관련이 있다.
    • 평균에서 표준편차보다 몇 배 더 떨어져 있다거나, 즉, 정규분포를 이루고 있지 않을 때
    • 왜도 또는 첨도가 발생할 때
  • 균등분포(Uniform Distribution)는, 발생할 확률이 모두 같다.
    • 만약, 확진자수가 최소 1부터 최대 10,000,000까지 균등하게 분포한다면, 어떤 값도 이상값으로 고려하지 않는다.
  • 이상값을 파악하려면, 반드시, 각 변수의 분포를 먼저 이해해야 한다.

라이브러리 및 데이터 불러오기

  • 실습을 위한 데이터를 불러온다.
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import statsmodels.api as sm
import scipy.stats as scistat

covidtotals = pd.read_csv("data/covidtotals.csv")
covidtotals.set_index("iso_code", inplace = True)

case_vars = ["location", "total_cases", "total_deaths", "total_cases_pm", "total_deaths_pm"]
demo_vars = ["population", "pop_density", "median_age", "gdp_per_capita", "hosp_beds"]

print(covidtotals.head())
            lastdate     location  total_cases  total_deaths  total_cases_pm  \
iso_code                                                                       
AFG       2020-06-01  Afghanistan        15205           257         390.589   
ALB       2020-06-01      Albania         1137            33         395.093   
DZA       2020-06-01      Algeria         9394           653         214.225   
AND       2020-06-01      Andorra          764            51        9888.048   
AGO       2020-06-01       Angola           86             4           2.617   

          total_deaths_pm  population  pop_density  median_age  \
iso_code                                                         
AFG                 6.602  38928341.0       54.422        18.6   
ALB                11.467   2877800.0      104.871        38.0   
DZA                14.891  43851043.0       17.348        29.1   
AND               660.066     77265.0      163.755         NaN   
AGO                 0.122  32866268.0       23.890        16.8   

          gdp_per_capita  hosp_beds  
iso_code                             
AFG             1803.987       0.50  
ALB            11803.431       2.89  
DZA            13913.839       1.90  
AND                  NaN        NaN  
AGO             5819.495        NaN  
  • describe() 함수를 통해 수치 데이터의 분포를 확인하도록 한다.
covid_case_df = covidtotals.loc[:, case_vars]
print(covid_case_df.describe())
        total_cases   total_deaths  total_cases_pm  total_deaths_pm
count  2.100000e+02     210.000000      210.000000       210.000000
mean   2.921614e+04    1770.714286     1355.357943        55.659129
std    1.363978e+05    8705.565857     2625.277497       144.785816
min    0.000000e+00       0.000000        0.000000         0.000000
25%    1.757500e+02       4.000000       92.541500         0.884750
50%    1.242500e+03      25.500000      280.928500         6.154000
75%    1.011700e+04     241.250000     1801.394750        31.777250
max    1.790191e+06  104383.000000    19771.348000      1237.551000
  • 백분위수(quantile)로 데이터를 표시한다.
print(covid_case_df.quantile(np.arange(0.0, 1.1, 0.1)))
     total_cases  total_deaths  total_cases_pm  total_deaths_pm
0.0          0.0           0.0          0.0000           0.0000
0.1         22.9           0.0         17.9986           0.0000
0.2        105.2           2.0         56.2910           0.3752
0.3        302.0           6.7        115.4341           1.7183
0.4        762.0          12.0        213.9734           3.9566
0.5       1242.5          25.5        280.9285           6.1540
0.6       2514.6          54.6        543.9562          12.2452
0.7       6959.8         137.2       1071.2442          25.9459
0.8      16847.2         323.2       2206.2982          49.9658
0.9      46513.1        1616.9       3765.1363         138.9045
1.0    1790191.0      104383.0      19771.3480        1237.5510
  • 왜도는 분포가 얼마나 대칭적인지를 나타냄
  • 왜도와 첨도는 어떻게 대칭적인지를 설명하며, 분포의 꼬리가 각각 얼마나 두꺼운지 나타냄.
covid_case_df.skew(axis=0, numeric_only = True)
total_cases        10.804275
total_deaths        8.929816
total_cases_pm      4.396091
total_deaths_pm     4.674417
dtype: float64
covid_case_df.kurtosis(axis=0, numeric_only = True)
total_cases        134.979577
total_deaths        95.737841
total_cases_pm      25.242790
total_deaths_pm     27.238232
dtype: float64
  • 정규성 검정을 테스트 한다.
  • p값 0.05미만에서 95% 수준에서 정규분포의 귀무가설을 기각하고, 대립가설을 채택한다.
    • 귀무가설: 표본의 모집단이 정규분포를 이루고 있다.
    • 대립가설: 표본의 모집단이 정규분포를 이루고 있지 않다.
scistat.shapiro(covid_case_df['total_cases'])
ShapiroResult(statistic=0.19379639625549316, pvalue=3.753789128593843e-29)
scistat.shapiro(covid_case_df['total_deaths'])
ShapiroResult(statistic=0.19832086563110352, pvalue=4.3427896631016077e-29)
scistat.shapiro(covid_case_df['total_cases_pm'])
ShapiroResult(statistic=0.5220695734024048, pvalue=1.3972683006509067e-23)
scistat.shapiro(covid_case_df['total_deaths_pm'])
ShapiroResult(statistic=0.41877639293670654, pvalue=1.361060423265974e-25)
  • 위 4개의 feature 모두 정규분포를 이루고 있지 않음을 확인할 수 있다.
  • 이번에는 qqplot을 그린다.
sm.qqplot(covid_case_df[["total_cases"]].sort_values(["total_cases"]), line = "s")
plt.title("QQ Plot of Total Cases")
Text(0.5, 1.0, 'QQ Plot of Total Cases')

png

Finding Missing Values

데이터 가져오기

import pandas as pd

covidtotals = pd.read_csv("data/covidtotalswithmissings.csv")
print(covidtotals.head())
  iso_code    lastdate     location  total_cases  total_deaths  \
0      AFG  2020-06-01  Afghanistan        15205           257   
1      ALB  2020-06-01      Albania         1137            33   
2      DZA  2020-06-01      Algeria         9394           653   
3      AND  2020-06-01      Andorra          764            51   
4      AGO  2020-06-01       Angola           86             4   

   total_cases_pm  total_deaths_pm  population  pop_density  median_age  \
0         390.589            6.602  38928341.0       54.422        18.6   
1         395.093           11.467   2877800.0      104.871        38.0   
2         214.225           14.891  43851043.0       17.348        29.1   
3        9888.048          660.066     77265.0      163.755         NaN   
4           2.617            0.122  32866268.0       23.890        16.8   

   gdp_per_capita  hosp_beds  
0        1803.987       0.50  
1       11803.431       2.89  
2       13913.839       1.90  
3             NaN        NaN  
4        5819.495        NaN  
  • Missing Value 확인
  • 일부 feature에서 missing value가 있는 것을 확인함.
covidtotals.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 210 entries, 0 to 209
Data columns (total 12 columns):
 #   Column           Non-Null Count  Dtype  
---  ------           --------------  -----  
 0   iso_code         210 non-null    object 
 1   lastdate         210 non-null    object 
 2   location         210 non-null    object 
 3   total_cases      210 non-null    int64  
 4   total_deaths     210 non-null    int64  
 5   total_cases_pm   209 non-null    float64
 6   total_deaths_pm  209 non-null    float64
 7   population       210 non-null    float64
 8   pop_density      198 non-null    float64
 9   median_age       186 non-null    float64
 10  gdp_per_capita   182 non-null    float64
 11  hosp_beds        164 non-null    float64
dtypes: float64(7), int64(2), object(3)
memory usage: 19.8+ KB
  • 데이터를 크게 두개의 기분으로 분류한다.
    • Covid case & Demographic Columns
case_vars = ["location", "total_cases", "total_deaths", "total_cases_pm", "total_deaths_pm"]
demo_vars = ["population", "pop_density", "median_age", "gdp_per_capita", "hosp_beds"]
  • axis 설정을 통해 인구통계와 Covid Cased의 결측치 값을 설정한다.
covidtotals[demo_vars].isnull().sum(axis=0)
population         0
pop_density       12
median_age        24
gdp_per_capita    28
hosp_beds         46
dtype: int64
covidtotals[case_vars].isnull().sum(axis=0)
location           0
total_cases        0
total_deaths       0
total_cases_pm     1
total_deaths_pm    1
dtype: int64
  • 이번에는 행 방향으로 발생한 결측치를 확인한다.
  • 결측치가 없는 행은 156개이고, 1개만 있는 행은 24개 순으로 집계 되었다.
demovars_misscnt = covidtotals[demo_vars].isnull().sum(axis=1)
demovars_misscnt.value_counts()
0    156
1     24
2     12
3     10
4      8
dtype: int64
  • 인구통계 데이터가 3가지 이상 누락된 국가를 나열한다.
    • 5개의 값만 추출했다.
print(covidtotals.loc[demovars_misscnt >= 3, ['location'] + demo_vars].head(5).T)
                     3         5                                24  \
location        Andorra  Anguilla  Bonaire Sint Eustatius and Saba   
population      77265.0   15002.0                          26221.0   
pop_density     163.755       NaN                              NaN   
median_age          NaN       NaN                              NaN   
gdp_per_capita      NaN       NaN                              NaN   
hosp_beds           NaN       NaN                              NaN   

                                    28              64  
location        British Virgin Islands  Faeroe Islands  
population                     30237.0         48865.0  
pop_density                    207.973          35.308  
median_age                         NaN             NaN  
gdp_per_capita                     NaN             NaN  
hosp_beds                          NaN             NaN  
  • 이번에는 코로나 사례 데이터에서 누락값을 확인한다.
    • 홍콩만 사례가 누락된 것을 확인할 수 있다.
totvars_misscnt = covidtotals[case_vars].isnull().sum(axis=1)
totvars_misscnt.value_counts()
0    209
2      1
dtype: int64
print(covidtotals.loc[totvars_misscnt == 2, ['location'] + case_vars].T)
                        87
location         Hong Kong
location         Hong Kong
total_cases              0
total_deaths             0
total_cases_pm         NaN
total_deaths_pm        NaN
print(covidtotals[covidtotals['location'] == "Hong Kong"])
   iso_code    lastdate   location  total_cases  total_deaths  total_cases_pm  \
87      HKG  2020-05-26  Hong Kong            0             0             NaN   

    total_deaths_pm  population  pop_density  median_age  gdp_per_capita  \
87              NaN   7496988.0     7039.714        44.8        56054.92   

    hosp_beds  
87        NaN  

방법 1. Inplace 사용

# 결측치 채우기
covidtotals = pd.read_csv("data/covidtotalswithmissings.csv")
covidtotals2 = covidtotals.copy()
covidtotals2[case_vars].isnull().sum(axis = 0) 
location           0
total_cases        0
total_deaths       0
total_cases_pm     1
total_deaths_pm    1
dtype: int64
covidtotals2.total_cases_pm.fillna(covidtotals2.total_cases/(covidtotals2.population/10000000), inplace = True)
covidtotals2.total_deaths_pm.fillna(covidtotals2.total_deaths/(covidtotals2.population/10000000), inplace = True)
covidtotals2[case_vars].isnull().sum(axis = 0) 
location           0
total_cases        0
total_deaths       0
total_cases_pm     0
total_deaths_pm    0
dtype: int64

방법 2. 매칭을 통한 대체

covidtotals = pd.read_csv("data/covidtotalswithmissings.csv")
covidtotals2 = covidtotals.copy()
covidtotals2[case_vars].isnull().sum(axis = 0) 
location           0
total_cases        0
total_deaths       0
total_cases_pm     1
total_deaths_pm    1
dtype: int64
covidtotals2.loc[:, 'total_cases_pm'] = covidtotals2.loc[:, 'total_cases_pm'].fillna(value=covidtotals2.total_cases/(covidtotals.population/10000000))
covidtotals2.loc[:, 'total_deaths_pm'] = covidtotals2.loc[:, 'total_deaths_pm'].fillna(value=covidtotals2.total_deaths/(covidtotals.population/10000000))
covidtotals2[case_vars].isnull().sum(axis = 0) 
location           0
total_cases        0
total_deaths       0
total_cases_pm     0
total_deaths_pm    0
dtype: int64

References

Walker, M. (2020). Python Data Cleaning Cookbook: Modern techniques and Python tools to detect and remove dirty data and extract key insights. Packt Publishing.

엑셀 데이터 가공하기 변환

강의 홍보

개요

  • 정리되지 못한 엑셀 파일을 불러와서 하나의 테이블을 만드는 과정을 진행해본다.

  • 위 데이터를 원본 그대로 받아서 pandas 데이터 프레임에 추가한다.
  • A3 셀에 있는 [시·도지사선거][서울특별시][강남구] 분리하여 각 column에 추가한다.

라이브러리 불러오기

  • 3개의 라이브러리를 불러온다.
import pandas as pd
import openpyxl
import os

파일 확인

  • data 폴더 내 데이터를 확인한다.
  • 추후, 엑셀 데이터만 추려서 반복문을 활용하여 동일하게 처리할 수 있도록 상상을 한다.
print(os.listdir('data'))
['1 강남구-[2021년_재·보궐선거]_개표단위별_개표결과.xlsx', '.DS_Store', '~$1 강남구-[2021년_재·보궐선거]_개표단위별_개표결과.xlsx']

openpyxl 라이브러리 할용

  • openpyxl 라이브러리는 A Python library to read/write Excel 2010 xlsx/xlsm files을 가지고 있다.
  • 먼저 A3 셀에 있는 [시·도지사선거][서울특별시][강남구] 데이터를 가져오도록 한다.
  • 이 때, openpyxl 라이브리를 활용하면 각 셀에 접근해서 개별적으로 데이터를 가져올 수 있다.
DATA_PATH = "data"
FILE_PATH = os.listdir(DATA_PATH)[0]
wb_obt= openpyxl.load_workbook("data/" + FILE_PATH) 
sheet = wb_obt.active
values = sheet["A3"].value
values
'[시·도지사선거][서울특별시][강남구]'

문자열 전처리

  • 먼저 하나의 셀로 연결되어 있는 것을 각각 분리하도록 하는 코드를 작성한다.
  • strip(‘pattern’)는 특정 문자를 제거하는 것이고, split(‘pattern’)는 문자열을 특수문자로 분리하는 것이다.
city_list = values.strip("[]").split("][")
city_list
['시·도지사선거', '서울특별시', '강남구']

데이터 수집 및 전처리

  • 이 때 중요한 parameters는 skiprows, header이다.
  • 먼저 skiprows는 특정 행은 건너 뛴다는 의미를 가지고 있다. 즉, 데이터프레임에 접근하기 전까지의 행은 건너 뛴다는 의미다.
  • header는 엑셀의 열에 해당하는데, 본 데이터에서는 multiple headers가 있다. 따라서, 이를 리스트 처리하면 해당 열은 모두 가져올 수 있다.
df = pd.read_excel("data/1 강남구-[2021년_재·보궐선거]_개표단위별_개표결과.xlsx", skiprows=3,  header=[0, 1], )
df.drop(columns=df.columns[-1], axis=1, inplace=True) # 마지막 column은 NAN라 삭제.  
print(df.head())
                읍면동명               투표구명               선거인수                투표수  \
  Unnamed: 0_level_1 Unnamed: 1_level_1 Unnamed: 2_level_1 Unnamed: 3_level_1   
0                 합계                              452344.0           276485.0   
1               거소투표                                3062.0             2877.0   
2             관외사전투표                               12957.0            12955.0   
3                신사동                 소계            13922.0             8672.0   
4                                관내사전투표             2667.0             2667.0   

     후보자별 득표수                                                               \
  더불어민주당\n박영선 국민의힘\n오세훈 기본소득당\n신지혜 국가혁명당\n허경영 미래당\n오태양 민생당\n이수봉 민생당\n이수봉.1   
0     66907.0  202320.0      909.0     2005.0    229.0    424.0        NaN   
1       595.0    1995.0       14.0       97.0     11.0     24.0        NaN   
2      4393.0    8171.0       48.0      100.0     18.0     18.0        NaN   
3      1581.0    6910.0       20.0       45.0      6.0     15.0        NaN   
4       632.0    1977.0        6.0       12.0      3.0      6.0        NaN   

                                                                        \
  신자유민주연합\n배영규 여성의당\n김진아 진보당\n송명숙 무소속\n정동희 무소속\n이도엽 무소속\n신지예         계   
0         20.0    1212.0    274.0     82.0     47.0    655.0  275084.0   
1          3.0       2.0      6.0      8.0      2.0      8.0    2765.0   
2          0.0      67.0     23.0      6.0      2.0     39.0   12885.0   
3          1.0      36.0      5.0      3.0      1.0     20.0    8643.0   
4          0.0       8.0      3.0      1.0      1.0      5.0    2654.0   

              무효\n투표수                 기권수  
  Unnamed: 18_level_1 Unnamed: 19_level_1  
0              1401.0            175859.0  
1               112.0               185.0  
2                70.0                 2.0  
3                29.0              5250.0  
4                13.0                 0.0  
  • header[0]값은 읍면동명, 투표구명, 선거인수, 투표수 등으로 정리가 되어 있다.
  • header[1]값은 각 후보들의 값이 나타난 것을 확인할 수 있다.
  • 여기에서 후보자별 득표수만 지우기만 하면 된다. 다만, 각각 가져와야 하는 값이 서로 다르다.
    • 이 때, MultiIndex에 대응하기 위해 get_level_values() 함수를 사용한다.
df.columns
MultiIndex([(    '읍면동명',  'Unnamed: 0_level_1'),
            (    '투표구명',  'Unnamed: 1_level_1'),
            (    '선거인수',  'Unnamed: 2_level_1'),
            (     '투표수',  'Unnamed: 3_level_1'),
            ('후보자별 득표수',         '더불어민주당\n박영선'),
            ('후보자별 득표수',           '국민의힘\n오세훈'),
            ('후보자별 득표수',          '기본소득당\n신지혜'),
            ('후보자별 득표수',          '국가혁명당\n허경영'),
            ('후보자별 득표수',            '미래당\n오태양'),
            ('후보자별 득표수',            '민생당\n이수봉'),
            ('후보자별 득표수',          '민생당\n이수봉.1'),
            ('후보자별 득표수',        '신자유민주연합\n배영규'),
            ('후보자별 득표수',           '여성의당\n김진아'),
            ('후보자별 득표수',            '진보당\n송명숙'),
            ('후보자별 득표수',            '무소속\n정동희'),
            ('후보자별 득표수',            '무소속\n이도엽'),
            ('후보자별 득표수',            '무소속\n신지예'),
            ('후보자별 득표수',                   '계'),
            ( '무효\n투표수', 'Unnamed: 18_level_1'),
            (     '기권수', 'Unnamed: 19_level_1')],
           )
df.columns.get_level_values(0)
Index(['읍면동명', '투표구명', '선거인수', '투표수', '후보자별 득표수', '후보자별 득표수', '후보자별 득표수',
       '후보자별 득표수', '후보자별 득표수', '후보자별 득표수', '후보자별 득표수', '후보자별 득표수', '후보자별 득표수',
       '후보자별 득표수', '후보자별 득표수', '후보자별 득표수', '후보자별 득표수', '후보자별 득표수', '무효\n투표수',
       '기권수'],
      dtype='object')
df.columns.get_level_values(1)
Index(['Unnamed: 0_level_1', 'Unnamed: 1_level_1', 'Unnamed: 2_level_1',
       'Unnamed: 3_level_1', '더불어민주당\n박영선', '국민의힘\n오세훈', '기본소득당\n신지혜',
       '국가혁명당\n허경영', '미래당\n오태양', '민생당\n이수봉', '민생당\n이수봉.1', '신자유민주연합\n배영규',
       '여성의당\n김진아', '진보당\n송명숙', '무소속\n정동희', '무소속\n이도엽', '무소속\n신지예', '계',
       'Unnamed: 18_level_1', 'Unnamed: 19_level_1'],
      dtype='object')
  • 이제, 각각의 index를 list로 변환후 하나의 column으로 합치는 과정을 진행한다.
  • 총 20개의 column이 정렬된 것을 확인할 수 있다.
col_1 = df.columns.get_level_values(0)[0:4].tolist()
col_2 = df.columns.get_level_values(1)[4:-2].tolist()
col_3 = df.columns.get_level_values(0)[-3:-1].tolist()
cols = col_1 + col_2 + col_3
cols
['읍면동명',
 '투표구명',
 '선거인수',
 '투표수',
 '더불어민주당\n박영선',
 '국민의힘\n오세훈',
 '기본소득당\n신지혜',
 '국가혁명당\n허경영',
 '미래당\n오태양',
 '민생당\n이수봉',
 '민생당\n이수봉.1',
 '신자유민주연합\n배영규',
 '여성의당\n김진아',
 '진보당\n송명숙',
 '무소속\n정동희',
 '무소속\n이도엽',
 '무소속\n신지예',
 '계',
 '후보자별 득표수',
 '무효\n투표수']
df.columns = cols
df.columns
Index(['읍면동명', '투표구명', '선거인수', '투표수', '더불어민주당\n박영선', '국민의힘\n오세훈', '기본소득당\n신지혜',
       '국가혁명당\n허경영', '미래당\n오태양', '민생당\n이수봉', '민생당\n이수봉.1', '신자유민주연합\n배영규',
       '여성의당\n김진아', '진보당\n송명숙', '무소속\n정동희', '무소속\n이도엽', '무소속\n신지예', '계',
       '후보자별 득표수', '무효\n투표수'],
      dtype='object')
  • 이제 시도와 시군구를 각각 추가한다.
df['시도'] = city_list[1]
df['시군구'] = city_list[2]
print(df.head(10))
     읍면동명    투표구명      선거인수       투표수  더불어민주당\n박영선  국민의힘\n오세훈  기본소득당\n신지혜  \
0      합계          452344.0  276485.0      66907.0   202320.0       909.0   
1    거소투표            3062.0    2877.0        595.0     1995.0        14.0   
2  관외사전투표           12957.0   12955.0       4393.0     8171.0        48.0   
3     신사동      소계   13922.0    8672.0       1581.0     6910.0        20.0   
4          관내사전투표    2667.0    2667.0        632.0     1977.0         6.0   
5          신사동제1투    2313.0    1052.0        205.0      827.0         1.0   
6          신사동제2투    1740.0     733.0        220.0      492.0         3.0   
7          신사동제3투    1896.0     813.0        261.0      508.0         6.0   
8          신사동제4투    2812.0    1736.0        112.0     1611.0         0.0   
9          신사동제5투    2494.0    1671.0        151.0     1495.0         4.0   

   국가혁명당\n허경영  미래당\n오태양  민생당\n이수봉  ...  여성의당\n김진아  진보당\n송명숙  무소속\n정동희  \
0      2005.0     229.0     424.0  ...     1212.0     274.0      82.0   
1        97.0      11.0      24.0  ...        2.0       6.0       8.0   
2       100.0      18.0      18.0  ...       67.0      23.0       6.0   
3        45.0       6.0      15.0  ...       36.0       5.0       3.0   
4        12.0       3.0       6.0  ...        8.0       3.0       1.0   
5         8.0       1.0       1.0  ...        4.0       0.0       1.0   
6        10.0       0.0       2.0  ...        5.0       0.0       0.0   
7         8.0       0.0       3.0  ...       14.0       2.0       0.0   
8         2.0       1.0       2.0  ...        2.0       0.0       1.0   
9         5.0       1.0       1.0  ...        3.0       0.0       0.0   

   무소속\n이도엽  무소속\n신지예         계  후보자별 득표수   무효\n투표수     시도  시군구  
0      47.0     655.0  275084.0    1401.0  175859.0  서울특별시  강남구  
1       2.0       8.0    2765.0     112.0     185.0  서울특별시  강남구  
2       2.0      39.0   12885.0      70.0       2.0  서울특별시  강남구  
3       1.0      20.0    8643.0      29.0    5250.0  서울특별시  강남구  
4       1.0       5.0    2654.0      13.0       0.0  서울특별시  강남구  
5       0.0       2.0    1050.0       2.0    1261.0  서울특별시  강남구  
6       0.0       1.0     733.0       0.0    1007.0  서울특별시  강남구  
7       0.0       6.0     809.0       4.0    1083.0  서울특별시  강남구  
8       0.0       1.0    1732.0       4.0    1076.0  서울특별시  강남구  
9       0.0       5.0    1665.0       6.0     823.0  서울특별시  강남구  

[10 rows x 22 columns]
  • 어느정도 정리가 된 것으로 보인다.

Pandas 속도 비교 - with or without Dictionary

강의 홍보

1줄 요약

  • Dictionary를 활용한 값 변경의 속도가 훨씬 빠르다.

데이터 불러오기

  • diamonds 데이터셋을 불러온다.
import pandas as pd
import seaborn as sns

diamonds = sns.load_dataset('diamonds')
print(diamonds)
       carat        cut color clarity  depth  table  price     x     y     z
0       0.23      Ideal     E     SI2   61.5   55.0    326  3.95  3.98  2.43
1       0.21    Premium     E     SI1   59.8   61.0    326  3.89  3.84  2.31
2       0.23       Good     E     VS1   56.9   65.0    327  4.05  4.07  2.31
3       0.29    Premium     I     VS2   62.4   58.0    334  4.20  4.23  2.63
4       0.31       Good     J     SI2   63.3   58.0    335  4.34  4.35  2.75
...      ...        ...   ...     ...    ...    ...    ...   ...   ...   ...
53935   0.72      Ideal     D     SI1   60.8   57.0   2757  5.75  5.76  3.50
53936   0.72       Good     D     SI1   63.1   55.0   2757  5.69  5.75  3.61
53937   0.70  Very Good     D     SI1   62.8   60.0   2757  5.66  5.68  3.56
53938   0.86    Premium     H     SI2   61.0   58.0   2757  6.15  6.12  3.74
53939   0.75      Ideal     D     SI2   62.2   55.0   2757  5.83  5.87  3.64

[53940 rows x 10 columns]
  • Color 데이터를 확인해보자.
diamonds['color'].value_counts()
G    11292
E     9797
F     9542
H     8304
D     6775
I     5422
J     2808
Name: color, dtype: int64

color 데이터 값 변경하기

  • D, E, F는 A로 바꿉니다.
  • G, H는 B로 바꿉니다.
  • I, J는 C로 바꿉니다.

Without Dictionary

  • 먼저 첫번째 방법입니다.
import time 

start_time = time.time()
diamonds['color'].replace('D', 'A', inplace=True)
diamonds['color'].replace('E', 'A', inplace=True)
diamonds['color'].replace('F', 'A', inplace=True)
diamonds['color'].replace('G', 'B', inplace=True)
diamonds['color'].replace('H', 'B', inplace=True)
diamonds['color'].replace('I', 'C', inplace=True)
diamonds['color'].replace('J', 'C', inplace=True)

print("Time using .replace() only: {} sec".format(time.time() - start_time))
print("---")
print(diamonds['color'].value_counts())
Time using .replace() only: 0.025814056396484375 sec
---
A    26114
B    19596
C     8230
Name: color, dtype: int64

With Dictionary

  • 이번에는 Dictionary를 활용합니다.
diamonds = sns.load_dataset('diamonds')

start_time = time.time()
diamonds.replace({'color': {'D':'A', 'E':'A', 'F':'A', 'G':'B', 'H':'B', 'I':'C', 'J':'C'}}, inplace=True)

print("Time using .replace() only: {} sec".format(time.time() - start_time))
print("---")
print(diamonds['color'].value_counts())
Time using .replace() only: 0.005134105682373047 sec
---
A    26114
B    19596
C     8230
Name: color, dtype: int64
  • 동일한 결괏값이 나왔지만, 속도 차이가 0.02초 vs 0.005초 차이로 매우 큼을 확인할 수 있다.
  • 즉, 값을 변경한다면, Dictionary를 사용한다.

Pandas 속도 비교 - loc vs replace(2)

강의 홍보

1줄 요약

  • 값을 변경할 때에는 .replace 메서드를 사용합니다.

개요

  • Replace 속도를 측정해보자.
  • 이번에는 multiple 값을 변경하는 방법에 대해 알아봅니다.

비교 1 .loc vs .replace

  • 값을 바꾸는 방법은 다음과 같다.
    • data['column'].loc[data['column'] == 'Old Value'] = 'New Value'
import pandas as pd
import seaborn as sns
diamonds = sns.load_dataset('diamonds')
print(diamonds)
       carat        cut color clarity  depth  table  price     x     y     z
0       0.23      Ideal     E     SI2   61.5   55.0    326  3.95  3.98  2.43
1       0.21    Premium     E     SI1   59.8   61.0    326  3.89  3.84  2.31
2       0.23       Good     E     VS1   56.9   65.0    327  4.05  4.07  2.31
3       0.29    Premium     I     VS2   62.4   58.0    334  4.20  4.23  2.63
4       0.31       Good     J     SI2   63.3   58.0    335  4.34  4.35  2.75
...      ...        ...   ...     ...    ...    ...    ...   ...   ...   ...
53935   0.72      Ideal     D     SI1   60.8   57.0   2757  5.75  5.76  3.50
53936   0.72       Good     D     SI1   63.1   55.0   2757  5.69  5.75  3.61
53937   0.70  Very Good     D     SI1   62.8   60.0   2757  5.66  5.68  3.56
53938   0.86    Premium     H     SI2   61.0   58.0   2757  6.15  6.12  3.74
53939   0.75      Ideal     D     SI2   62.2   55.0   2757  5.83  5.87  3.64

[53940 rows x 10 columns]
diamonds.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 53940 entries, 0 to 53939
Data columns (total 10 columns):
 #   Column   Non-Null Count  Dtype   
---  ------   --------------  -----   
 0   carat    53940 non-null  float64 
 1   cut      53940 non-null  category
 2   color    53940 non-null  category
 3   clarity  53940 non-null  category
 4   depth    53940 non-null  float64 
 5   table    53940 non-null  float64 
 6   price    53940 non-null  int64   
 7   x        53940 non-null  float64 
 8   y        53940 non-null  float64 
 9   z        53940 non-null  float64 
dtypes: category(3), float64(6), int64(1)
memory usage: 3.0 MB

비교 2. .loc vs .replace

  • cut Column에 있는 값 중, PremiumIdeal 모두 Best로 변경합니다.
import time

diamonds = sns.load_dataset('diamonds')
diamonds['cut'] = diamonds['cut'].astype('object')

start_time = time.time()
diamonds['cut'].loc[(diamonds['cut'] == 'Premium') | (diamonds['cut'] == 'Ideal')] = 'Best'
print('Time using .loc[]: {} sec'.format(time.time() - start_time))
print(diamonds)
Time using .loc[]: 0.008001089096069336 sec
       carat        cut color clarity  depth  table  price     x     y     z
0       0.23       Best     E     SI2   61.5   55.0    326  3.95  3.98  2.43
1       0.21       Best     E     SI1   59.8   61.0    326  3.89  3.84  2.31
2       0.23       Good     E     VS1   56.9   65.0    327  4.05  4.07  2.31
3       0.29       Best     I     VS2   62.4   58.0    334  4.20  4.23  2.63
4       0.31       Good     J     SI2   63.3   58.0    335  4.34  4.35  2.75
...      ...        ...   ...     ...    ...    ...    ...   ...   ...   ...
53935   0.72       Best     D     SI1   60.8   57.0   2757  5.75  5.76  3.50
53936   0.72       Good     D     SI1   63.1   55.0   2757  5.69  5.75  3.61
53937   0.70  Very Good     D     SI1   62.8   60.0   2757  5.66  5.68  3.56
53938   0.86       Best     H     SI2   61.0   58.0   2757  6.15  6.12  3.74
53939   0.75       Best     D     SI2   62.2   55.0   2757  5.83  5.87  3.64

[53940 rows x 10 columns]


/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pandas/core/indexing.py:1636: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  self._setitem_single_block(indexer, value, name)
  • 이번에는 replace 메서드를 사용해본다.
    • data['column'].replace('old value', 'new value', inplace = True
diamonds = sns.load_dataset('diamonds')

start_time = time.time()
diamonds.replace(['Premium', 'Ideal'], 'Best', inplace=True)
print('Time using replace(): {} sec'.format(time.time() - start_time)) 
Time using replace(): 0.0011608600616455078 sec
  • 기존 코드에서, GoodVery GoodLow로 변경코드를 추가합니다.
diamonds = sns.load_dataset('diamonds')
diamonds['cut'] = diamonds['cut'].astype('object')

start_time = time.time()
diamonds['cut'].loc[(diamonds['cut'] == 'Premium') | \
                    (diamonds['cut'] == 'Ideal')] = 'Best'
diamonds['cut'].loc[(diamonds['cut'] == 'Very Good') | \
                    (diamonds['cut'] == 'Good')] = 'Low'
print('Time using .loc[]: {} sec'.format(time.time() - start_time))
print(diamonds)
Time using .loc[]: 0.013423681259155273 sec
       carat   cut color clarity  depth  table  price     x     y     z
0       0.23  Best     E     SI2   61.5   55.0    326  3.95  3.98  2.43
1       0.21  Best     E     SI1   59.8   61.0    326  3.89  3.84  2.31
2       0.23   Low     E     VS1   56.9   65.0    327  4.05  4.07  2.31
3       0.29  Best     I     VS2   62.4   58.0    334  4.20  4.23  2.63
4       0.31   Low     J     SI2   63.3   58.0    335  4.34  4.35  2.75
...      ...   ...   ...     ...    ...    ...    ...   ...   ...   ...
53935   0.72  Best     D     SI1   60.8   57.0   2757  5.75  5.76  3.50
53936   0.72   Low     D     SI1   63.1   55.0   2757  5.69  5.75  3.61
53937   0.70   Low     D     SI1   62.8   60.0   2757  5.66  5.68  3.56
53938   0.86  Best     H     SI2   61.0   58.0   2757  6.15  6.12  3.74
53939   0.75  Best     D     SI2   62.2   55.0   2757  5.83  5.87  3.64

[53940 rows x 10 columns]


/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pandas/core/indexing.py:1636: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  self._setitem_single_block(indexer, value, name)
diamonds = sns.load_dataset('diamonds')

start_time = time.time()
diamonds.replace([['Premium', 'Ideal'], ['Very Good', 'Good']], ['Best', 'Low'], inplace=True)
print('Time using replace(): {} sec'.format(time.time() - start_time)) 
print(diamonds)
Time using replace(): 0.002335071563720703 sec
       carat   cut color clarity  depth  table  price     x     y     z
0       0.23  Best     E     SI2   61.5   55.0    326  3.95  3.98  2.43
1       0.21  Best     E     SI1   59.8   61.0    326  3.89  3.84  2.31
2       0.23   Low     E     VS1   56.9   65.0    327  4.05  4.07  2.31
3       0.29  Best     I     VS2   62.4   58.0    334  4.20  4.23  2.63
4       0.31   Low     J     SI2   63.3   58.0    335  4.34  4.35  2.75
...      ...   ...   ...     ...    ...    ...    ...   ...   ...   ...
53935   0.72  Best     D     SI1   60.8   57.0   2757  5.75  5.76  3.50
53936   0.72   Low     D     SI1   63.1   55.0   2757  5.69  5.75  3.61
53937   0.70   Low     D     SI1   62.8   60.0   2757  5.66  5.68  3.56
53938   0.86  Best     H     SI2   61.0   58.0   2757  6.15  6.12  3.74
53939   0.75  Best     D     SI2   62.2   55.0   2757  5.83  5.87  3.64

[53940 rows x 10 columns]

Pandas 속도 비교 - loc vs replace

강의 홍보

개요

  • loc and Replace 속도를 비교 측정해본다..

방법 1. .loc vs .replace

  • 값을 바꾸는 방법은 다음과 같다.
    • data['column'].loc[data['column'] == 'Old Value'] = 'New Value'
import pandas as pd
import seaborn as sns
diamonds = sns.load_dataset('diamonds')
print(diamonds)
       carat        cut color clarity  depth  table  price     x     y     z
0       0.23      Ideal     E     SI2   61.5   55.0    326  3.95  3.98  2.43
1       0.21    Premium     E     SI1   59.8   61.0    326  3.89  3.84  2.31
2       0.23       Good     E     VS1   56.9   65.0    327  4.05  4.07  2.31
3       0.29    Premium     I     VS2   62.4   58.0    334  4.20  4.23  2.63
4       0.31       Good     J     SI2   63.3   58.0    335  4.34  4.35  2.75
...      ...        ...   ...     ...    ...    ...    ...   ...   ...   ...
53935   0.72      Ideal     D     SI1   60.8   57.0   2757  5.75  5.76  3.50
53936   0.72       Good     D     SI1   63.1   55.0   2757  5.69  5.75  3.61
53937   0.70  Very Good     D     SI1   62.8   60.0   2757  5.66  5.68  3.56
53938   0.86    Premium     H     SI2   61.0   58.0   2757  6.15  6.12  3.74
53939   0.75      Ideal     D     SI2   62.2   55.0   2757  5.83  5.87  3.64

[53940 rows x 10 columns]
  • cut Column에 있는 값 중, Premium을 Best로 바꿔보도록 한다.
import time
start_time = time.time()

diamonds['cut'].loc[diamonds['cut'] == 'Premium'] == 'Best'
print('Time using .loc[]: {} sec'.format(time.time() - start_time))
Time using .loc[]: 0.0020329952239990234 sec
  • 이번에는 replace 메서드를 사용해본다.
    • data['column'].replace('old value', 'new value', inplace = True
diamonds = sns.load_dataset('diamonds')

start_time = time.time()
diamonds.replace('Premium', 'Best', inplace=True)
print('Time using replace(): {} sec'.format(time.time() - start_time))
Time using replace(): 0.00027108192443847656 sec

Pandas 속도 비교 - iloc and loc

강의 홍보

1줄 요약

  • .loc[]와 .iloc[] 인덱스의 속도 차이를 측정해본다.

개요

  • 시간이 허락한다면, Pandas 속도를 비교하는 게시글을 자주 작성하려고 한다.
    • Pandas가 상대적으로 속도가 느리기 때문에, 조금 더 효율적인 코드를 작성하는 쪽에 초점을 맞춰본다.
  • .loc[] : index name locator를 의미한다.
  • iloc[] : index number locator를 의미한다.

행 선택시 속도 비교

  • 먼저 행을 선택할 때의 속도 차이를 확인하도록 합니다.
import pandas as pd
import time
import seaborn as sns

diamonds = sns.load_dataset("diamonds")
diamonds.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 53940 entries, 0 to 53939
Data columns (total 10 columns):
 #   Column   Non-Null Count  Dtype   
---  ------   --------------  -----   
 0   carat    53940 non-null  float64 
 1   cut      53940 non-null  category
 2   color    53940 non-null  category
 3   clarity  53940 non-null  category
 4   depth    53940 non-null  float64 
 5   table    53940 non-null  float64 
 6   price    53940 non-null  int64   
 7   x        53940 non-null  float64 
 8   y        53940 non-null  float64 
 9   z        53940 non-null  float64 
dtypes: category(3), float64(6), int64(1)
memory usage: 3.0 MB
  • 먼저 .loc 속도 측정을 해봅니다.
row_nums = range(0, 10000)

start_time = time.time()
rows = diamonds.loc[row_nums]
end_time = time.time()

print("Time using .loc[]: {} sec".format(end_time - start_time))
Time using .loc[]: 0.0029916763305664062 sec
  • 이번에는 동일하게 .iloc를 적용해봅니다.
start_time = time.time()
rows = diamonds.iloc[row_nums]
end_time = time.time()

print("Time using .iloc[]: {} sec".format(end_time - start_time))
Time using .iloc[]: 0.001990079879760742 sec

열 선택시 속도 비교

  • 이번에는 iloc를 활용하여 열을 선택합니다.
iloc_start_time = time.time()
cols = diamonds.iloc[:, [0, 2, 4, 6, 8]]
iloc_end_time = time.time()

print("Time using .iloc[]: {} sec".format(iloc_end_time - iloc_start_time))
Time using .iloc[]: 0.0009975433349609375 sec
  • 이번에는 Column명을 입력해서 추출하도록 합니다.
name_start_time = time.time()
cols = diamonds[['carat', 'color', 'depth', 'price', 'y']]
name_end_time = time.time()

print("Time using selection by name : {} sec".format(name_end_time - name_start_time))
Time using selection by name : 0.000997304916381836 sec

Reference

Pandas read_csv skiprows 활용

강의 홍보

문제 개요

  • Kaggle 데이터 New York City Taxi Fare Prediction 데이터를 구글 코랩에서 Loading 하는 중 메모리 문제가 발생함
  • 계통추출(Systematic Sampling)을 통해 데이터를 불러오기로 함

예제 실습

  • 아래 예제를 통해서 실제로 데이터가 줄어드는지 확인을 해본다.
  • 핵심 코드는 skip_logic 함수이며, skiprows = skiprows=lambda x: skip_logic(x, 3) 형태로 작성할 수 있다.
  • IRIS 데이터는 https://www.kaggle.com/saurabh00007/iriscsv 에서 다운로드 받았다.
    • iris 데이터외에도 각자 데이터를 가지고 실습을 해도 좋다.
import pandas as pd 

def skip_logic(index, skip_num):
    if index % skip_num == 0:
        return False
    return True

def main():
    print('**** skiprows 기본 옵션 ****')
    iris = pd.read_csv('iris.csv')
    print(iris.shape)

    print('**** skiprows 인덱스 0, 2, 5만 제외 ****')
    iris = pd.read_csv('iris.csv', skiprows=[0, 2, 5])
    print(iris.shape)
    
    print('**** skiprows 인덱스 range(3, 20)만 제외 ****')
    iris = pd.read_csv('iris.csv', skiprows=[i for i in range(3, 20)])
    print(iris.shape)
    
    print('**** skiprows 입력값의 배수에 해당하는 값만 Load ****')
    iris = pd.read_csv('iris.csv', skiprows=lambda x: skip_logic(x, 3))
    print(iris.shape)
    
if __name__ == '__main__':
    main()
**** skiprows 기본 옵션 ****
(150, 6)
**** skiprows 인덱스 0, 2, 5만 제외 ****
(147, 6)
**** skiprows 인덱스 range(3, 20)만 제외 ****
(133, 6)
**** skiprows 입력값의 배수에 해당하는 값만 Load ****
(50, 6)

실전 적용

  • 이제 배운 것을 적용해보자.

데이터 크기

  • train.csv 데이터의 크기를 확인해보자.
import os

def convert_bytes(file_path, unit=None):
  size = os.path.getsize(file_path)
  if unit == "KB":
    return print('File size: ' + str(round(size / 1024, 3)) + ' Kilobytes')
  elif unit == "MB":
    return print('File size: ' + str(round(size / (1024 * 1024), 3)) + ' Megabytes')
  elif unit == "GB":
    return print('File size: ' + str(round(size / (1024 * 1024 * 1024), 3)) + ' Gigabytes')
  else:
    return print('File size: ' + str(size) + ' bytes')

file_list = ['train.csv', 'test.csv', 'sample_submission.csv']
for file in file_list:
  print("The {file} size: ".format(file=file))
  convert_bytes(file)
  convert_bytes(file, 'KB')
  convert_bytes(file, 'MB')
  convert_bytes(file, 'GB')
  print("--" * 5)
The train.csv size: 
File size: 5697178298 bytes
File size: 5563650.682 Kilobytes
File size: 5433.253 Megabytes
File size: 5.306 Gigabytes
----------
The test.csv size: 
File size: 983020 bytes
File size: 959.98 Kilobytes
File size: 0.937 Megabytes
File size: 0.001 Gigabytes
----------
The sample_submission.csv size: 
File size: 343271 bytes
File size: 335.226 Kilobytes
File size: 0.327 Megabytes
File size: 0.0 Gigabytes
----------

실전 적용

  • 이제 실전 적용을 해본다.
import numpy as np
import pandas as pd
import seaborn as sns 
import matplotlib.pyplot as plt

def skip_logic(index, skip_num):
    if index % skip_num == 0:
        return False
    return True

train = pd.read_csv('./train.csv', skiprows=lambda x: skip_logic(x, 4))
print(train.shape)
test = pd.read_csv('./test.csv')
submission = pd.read_csv('./sample_submission.csv')
(13855964, 8)

결론

  • 대용량 데이터를 다루는 것은 쉽지 않지만, skiprows 파라미터를 적절히 활용하여 메모리 이슈를 피하자.

List to Pandas

강의 홍보

개요

  • List는 파이썬 데이터 타입의 기본 자료형이다.
  • Pandas 데이터 분석을 위한 기본적인 자료형이다.
  • List에서 Pandas로 변환하는 작업의 다양한 방법을 활용해본다.

방법 1. 기초

  • List를 생성한 후, 데이터프레임으로 변환한다.
  • 여기에서는 columnindex값을 확인해본다.
import pandas as pd

lst = ["Korea", "Japan", "USA", "China", "Russia"]
data = pd.DataFrame(lst)
print(data)
        0
0   Korea
1   Japan
2     USA
3   China
4  Russia

방법 2. Column & Index 추가

  • 이번에는 columnindex를 추가한다.
lst = ["Korea", "Japan", "USA", "China", "Russia"]
country_index = ["a", "b", "c", "d", "e"]
data = pd.DataFrame(lst, index = country_index, columns=["Country"])
print(data)
  Country
a   Korea
b   Japan
c     USA
d   China
e  Russia

방법 3. 두개의 리스트와 Zip 활용

  • 이번에는 zip()함수를 활용하기에 앞서서, zip() 함수를 이해한다.
a = ["김", "심", "홍"]
b = ["길동", "청이", "길동"]

x = zip(a, b)
print(list(x))
[('김', '길동'), ('심', '청이'), ('홍', '길동')]
  • 위 결과값이 말해주는 것처럼 동일한 개수로 이루어진 자료향을 묶어 주는 역할을 한다.
  • 이를 활용하여 두개의 리스트를 판다스 데이터프레임으로 변환해준다.
full_name = pd.DataFrame(list(zip(a, b)), 
                         columns = ["성", "이름"])

print(full_name)
   성  이름
0  김  길동
1  심  청이
2  홍  길동

방법 4. Dictionary 활용

  • ListDictionary를 활용하여 데이터 프레임을 작성한다.
# 3개의 리스트
name = ["홍길동", "심청이", "임꺽정"]
age = [30, 40, 50]
gender = ["남성", "여성", "남성"]

# 딕셔너리 생성
dict = {"성함": name, "나이": age, "성별": gender}

class_df = pd.DataFrame(dict)
print(class_df)
    성함  나이  성별
0  홍길동  30  남성
1  심청이  40  여성
2  임꺽정  50  남성

엑셀로 내보내기

  • 이렇게 생성된 데이터를 엑셀로 내보내기를 해본다.
class_df.to_excel("class_df.xlsx", sheet_name='1반', index = False)
  • 실제로 내보내기가 되었는지 확인해본다.
%ls
class_df.xlsx 

Reference

Shivam_k. (2018). Create a Pandas DataFrame from Lists. Retreived from https://www.geeksforgeeks.org/create-a-pandas-dataframe-from-lists/