[Python] Data manipulation with pandas(3)
          
  
    
    
    
      
      
      
        
        
          5 minute read
        
      
    
  
        
      
      
        
          
        
        
Pandas
  - Slicing and Indexing DataFrames
    
      - pandas에는 index와 loc을 사용해 더 깔끔한 코드로 subsetting을 수 있음
- 그러나 data가 index로 활용된 측면에서 이는 tidy data는 아님(이 문제는 .reset_index를 통해 해결 가능)
- 개인적으로 dataframe 정렬 후 slicing을 통해 dataframe을 subsetting할 때 유용할 수 있을 것이라고 생각
- .set_index()
- .reset_index()
- .loc()
- .sort_index()
 
# import data
import seaborn as sns
import pandas as pd
import numpy as np
# pd.set_option('display.max_rows', 1000)
# pd.set_option('display.max_columns', 1000)
iris = sns.load_dataset("iris")
temperatures = pd.read_csv("temperatures.csv")
temperatures.head(3)
  
    
      |  | Unnamed: 0 | date | city | country | avg_temp_c | 
  
  
    
      | 0 | 0 | 2000-01-01 | Abidjan | Côte D'Ivoire | 27.293 | 
    
      | 1 | 1 | 2000-02-01 | Abidjan | Côte D'Ivoire | 27.685 | 
    
      | 2 | 2 | 2000-03-01 | Abidjan | Côte D'Ivoire | 29.061 | 
  
 
.set_index()
  - index는 multilevel(2개 이상)으로 설정할 수 있음
# index가 1개인 경우
iris["name"] = iris["species"] + np.arange(len(iris)).astype(str)
iris.set_index("name").head()
  
    
      |  | sepal_length | sepal_width | petal_length | petal_width | species | 
    
      | name |  |  |  |  |  | 
  
  
    
      | setosa0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa | 
    
      | setosa1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa | 
    
      | setosa2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa | 
    
      | setosa3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa | 
    
      | setosa4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa | 
  
 
# index가 2개인 경우
temperatures_multi_ind = temperatures.set_index(["country", "city"])
temperatures_multi_ind.head()
  
    
      |  |  | Unnamed: 0 | date | avg_temp_c | 
    
      | country | city |  |  |  | 
  
  
    
      | Côte D'Ivoire | Abidjan | 0 | 2000-01-01 | 27.293 | 
    
      | Abidjan | 1 | 2000-02-01 | 27.685 | 
    
      | Abidjan | 2 | 2000-03-01 | 29.061 | 
    
      | Abidjan | 3 | 2000-04-01 | 28.162 | 
    
      | Abidjan | 4 | 2000-05-01 | 27.547 | 
  
 
.reset_index()
  - index가 다시 column으로 바뀜
- 옵션 drop=True로 할 경우 index 열 제거
temperatures_ind = temperatures.set_index("city")
temperatures_ind.head(3)
  
    
      |  | Unnamed: 0 | date | country | avg_temp_c | 
    
      | city |  |  |  |  | 
  
  
    
      | Abidjan | 0 | 2000-01-01 | Côte D'Ivoire | 27.293 | 
    
      | Abidjan | 1 | 2000-02-01 | Côte D'Ivoire | 27.685 | 
    
      | Abidjan | 2 | 2000-03-01 | Côte D'Ivoire | 29.061 | 
  
 
temperatures_ind.reset_index().head(3)
  
    
      |  | city | Unnamed: 0 | date | country | avg_temp_c | 
  
  
    
      | 0 | Abidjan | 0 | 2000-01-01 | Côte D'Ivoire | 27.293 | 
    
      | 1 | Abidjan | 1 | 2000-02-01 | Côte D'Ivoire | 27.685 | 
    
      | 2 | Abidjan | 2 | 2000-03-01 | Côte D'Ivoire | 29.061 | 
  
 
temperatures_ind.reset_index(drop=True).head(3)
  
    
      |  | Unnamed: 0 | date | country | avg_temp_c | 
  
  
    
      | 0 | 0 | 2000-01-01 | Côte D'Ivoire | 27.293 | 
    
      | 1 | 1 | 2000-02-01 | Côte D'Ivoire | 27.685 | 
    
      | 2 | 2 | 2000-03-01 | Côte D'Ivoire | 29.061 | 
  
 
.loc()을 이용한 subsetting(1)
# .loc을 활용해 (row에 대해) filter하는 방법1 (df.column으로 조건을 주면 됨) (추천)
iris.loc[(iris.species == "setosa") & (iris.sepal_length < 5.0)].head()
  
    
      |  | sepal_length | sepal_width | petal_length | petal_width | species | name | 
  
  
    
      | 1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa | setosa1 | 
    
      | 2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa | setosa2 | 
    
      | 3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa | setosa3 | 
    
      | 6 | 4.6 | 3.4 | 1.4 | 0.3 | setosa | setosa6 | 
    
      | 8 | 4.4 | 2.9 | 1.4 | 0.2 | setosa | setosa8 | 
  
 
# .loc을 활용해 (row에 대해) filter하는 방법2 (filter할 것을 index로 설정 후 조건을 주면 됨)
temperatures_ind = temperatures.set_index("city")
temperatures_ind = temperatures_ind.loc[["Moscow", "Saint Petersburg"]]
temperatures_ind.reset_index().head(5)
# 방법1로 방법2를 할 경우의 코드
# temperatures.loc[temperatures.city.isin(["Moscow", "Saint Petersburg"])].head(5)
  
    
      |  | city | Unnamed: 0 | date | country | avg_temp_c | year | 
  
  
    
      | 0 | Moscow | 10725 | 2000-01-01 | Russia | -7.313 | 2000 | 
    
      | 1 | Moscow | 10726 | 2000-02-01 | Russia | -3.551 | 2000 | 
    
      | 2 | Moscow | 10727 | 2000-03-01 | Russia | -1.661 | 2000 | 
    
      | 3 | Moscow | 10728 | 2000-04-01 | Russia | 10.096 | 2000 | 
    
      | 4 | Moscow | 10729 | 2000-05-01 | Russia | 10.357 | 2000 | 
  
 
# .loc을 활용해 (row에 대해 ) filter 하는 방법 2-2 (multi index인 경우, 리스트 안에 튜플로(outer_index, inner_index) 조건을 주어야 함)
temperatures_multi_ind = temperatures.set_index(["country", "city"])
rows_to_keep = [("Brazil", "Rio De Janeiro"), ("Pakistan", "Lahore")]
temperatures_multi_ind.loc[rows_to_keep]
# 방법1로 방법 2-2를 할 경우의 코드
# temperatures[(temperatures.country.isin(["Brazil", "Pakistan"])) & (temperatures.city.isin(["Rio De Janeiro", "Lahore"]))]
  
    
      |  |  | Unnamed: 0 | date | avg_temp_c | 
    
      | country | city |  |  |  | 
  
  
    
      | Brazil | Rio De Janeiro | 12540 | 2000-01-01 | 25.974 | 
    
      | Rio De Janeiro | 12541 | 2000-02-01 | 26.699 | 
    
      | Rio De Janeiro | 12542 | 2000-03-01 | 26.270 | 
    
      | Rio De Janeiro | 12543 | 2000-04-01 | 25.750 | 
    
      | Rio De Janeiro | 12544 | 2000-05-01 | 24.356 | 
    
      | ... | ... | ... | ... | ... | 
    
      | Pakistan | Lahore | 8575 | 2013-05-01 | 33.457 | 
    
      | Lahore | 8576 | 2013-06-01 | 34.456 | 
    
      | Lahore | 8577 | 2013-07-01 | 33.279 | 
    
      | Lahore | 8578 | 2013-08-01 | 31.511 | 
    
      | Lahore | 8579 | 2013-09-01 | NaN | 
  
330 rows × 3 columns
 
.loc()을 이용한 subsetting(2)
  - row에 대한 filter & column에 대한 select
temperatures_srt = temperatures.set_index(["country", "city"]).sort_index()
# .loc을 활용해 (row에 대해) slice 하는 방법 1 (index slicing ("condition1":"condition2")로 조건을 주면 됨)
temperatures_srt.loc["Pakistan":"Russia"]
# multi index인 경우, inner index에 대해서만 조건을 줄 수 없음, 아래의 겨우 원하는 df를 반환하지 않음
# temperatures_srt.loc["Lahore":"Moscow"]
  
    
      |  |  | Unnamed: 0 | date | avg_temp_c | year | 
    
      | country | city |  |  |  |  | 
  
  
    
      | Pakistan | Faisalabad | 4785 | 2000-01-01 | 12.792 | 2000 | 
    
      | Faisalabad | 4786 | 2000-02-01 | 14.339 | 2000 | 
    
      | Faisalabad | 4787 | 2000-03-01 | 20.309 | 2000 | 
    
      | Faisalabad | 4788 | 2000-04-01 | 29.072 | 2000 | 
    
      | Faisalabad | 4789 | 2000-05-01 | 34.845 | 2000 | 
    
      | ... | ... | ... | ... | ... | ... | 
    
      | Russia | Saint Petersburg | 13360 | 2013-05-01 | 12.355 | 2013 | 
    
      | Saint Petersburg | 13361 | 2013-06-01 | 17.185 | 2013 | 
    
      | Saint Petersburg | 13362 | 2013-07-01 | 17.234 | 2013 | 
    
      | Saint Petersburg | 13363 | 2013-08-01 | 17.153 | 2013 | 
    
      | Saint Petersburg | 13364 | 2013-09-01 | NaN | 2013 | 
  
1155 rows × 4 columns
 
# .loc을 활용해 (row에 대해) slice 하는 방법 1-2 (multi index인 경우, 리스트 안에 튜플:튜플로("outer_index", "inner_index"):("outer_index", "inner_index") 조건을 주어야 함)
temperatures_srt.loc[("Pakistan","Lahore"):("Russia","Moscow")]
  
    
      |  |  | Unnamed: 0 | date | avg_temp_c | 
    
      | country | city |  |  |  | 
  
  
    
      | Pakistan | Lahore | 8415 | 2000-01-01 | 12.792 | 
    
      | Lahore | 8416 | 2000-02-01 | 14.339 | 
    
      | Lahore | 8417 | 2000-03-01 | 20.309 | 
    
      | Lahore | 8418 | 2000-04-01 | 29.072 | 
    
      | Lahore | 8419 | 2000-05-01 | 34.845 | 
    
      | ... | ... | ... | ... | ... | 
    
      | Russia | Moscow | 10885 | 2013-05-01 | 16.152 | 
    
      | Moscow | 10886 | 2013-06-01 | 18.718 | 
    
      | Moscow | 10887 | 2013-07-01 | 18.136 | 
    
      | Moscow | 10888 | 2013-08-01 | 17.485 | 
    
      | Moscow | 10889 | 2013-09-01 | NaN | 
  
660 rows × 3 columns
 
temperatures_srt = temperatures.set_index(["country", "city"]).sort_index()
# .loc을 활용해 (column)에 대해 slice 하는 방법 2
temperatures_srt.loc[:,'date':'avg_temp_c']
  
    
      |  |  | date | avg_temp_c | 
    
      | country | city |  |  | 
  
  
    
      | Afghanistan | Kabul | 2000-01-01 | 3.326 | 
    
      | Kabul | 2000-02-01 | 3.454 | 
    
      | Kabul | 2000-03-01 | 9.612 | 
    
      | Kabul | 2000-04-01 | 17.925 | 
    
      | Kabul | 2000-05-01 | 24.658 | 
    
      | ... | ... | ... | ... | 
    
      | Zimbabwe | Harare | 2013-05-01 | 18.298 | 
    
      | Harare | 2013-06-01 | 17.020 | 
    
      | Harare | 2013-07-01 | 16.299 | 
    
      | Harare | 2013-08-01 | 19.232 | 
    
      | Harare | 2013-09-01 | NaN | 
  
16500 rows × 2 columns
 
temperatures_srt = temperatures.set_index(["country", "city"]).sort_index()
# .loc을 활용해 (row와 column)에 대해 slice 하는 방법 2
temperatures_srt.loc[("India", "Hyderabad"):("Iraq", "Baghdad"),"date":"avg_temp_c"]
  
    
      |  |  | date | avg_temp_c | 
    
      | country | city |  |  | 
  
  
    
      | India | Hyderabad | 2000-01-01 | 23.779 | 
    
      | Hyderabad | 2000-02-01 | 25.826 | 
    
      | Hyderabad | 2000-03-01 | 28.821 | 
    
      | Hyderabad | 2000-04-01 | 32.698 | 
    
      | Hyderabad | 2000-05-01 | 32.438 | 
    
      | ... | ... | ... | ... | 
    
      | Iraq | Baghdad | 2013-05-01 | 28.673 | 
    
      | Baghdad | 2013-06-01 | 33.803 | 
    
      | Baghdad | 2013-07-01 | 36.392 | 
    
      | Baghdad | 2013-08-01 | 35.463 | 
    
      | Baghdad | 2013-09-01 | NaN | 
  
2145 rows × 2 columns
 
.sort_index
temperatures_ind = temperatures.set_index(["country", "city"])
temperatures_ind.sort_index(level=["country", "city"], ascending=[True,False]).reset_index(drop=True)
# 위와 같음
# temperatures.sort_values(["country", "city"], ascending = [True,False])[["Unnamed: 0", "date", "avg_temp_c"]]
  
    
      |  | Unnamed: 0 | date | avg_temp_c | 
  
  
    
      | 0 | 7260 | 2000-01-01 | 3.326 | 
    
      | 1 | 7261 | 2000-02-01 | 3.454 | 
    
      | 2 | 7262 | 2000-03-01 | 9.612 | 
    
      | 3 | 7263 | 2000-04-01 | 17.925 | 
    
      | 4 | 7264 | 2000-05-01 | 24.658 | 
    
      | ... | ... | ... | ... | 
    
      | 16495 | 5605 | 2013-05-01 | 18.298 | 
    
      | 16496 | 5606 | 2013-06-01 | 17.020 | 
    
      | 16497 | 5607 | 2013-07-01 | 16.299 | 
    
      | 16498 | 5608 | 2013-08-01 | 19.232 | 
    
      | 16499 | 5609 | 2013-09-01 | NaN | 
  
16500 rows × 3 columns
 
.iloc을 이용한 pivot_table subsetting
# dataframe["column"].dt.component을 통해 date의 year, month, day 등을 얻을 수 있음
temperatures["date"] = pd.to_datetime(temperatures["date"])
temperatures["year"] = temperatures["date"].dt.year
temp_by_country_city_vs_year = temperatures.pivot_table(
    values = "avg_temp_c", 
    index = ["country", "city"], 
    columns = "year")
temp_by_country_city_vs_year.loc[("Egypt", "Cairo"):("India", "Delhi"), "2005":"2010"]
  
    
      |  | year | 2005 | 2006 | 2007 | 2008 | 2009 | 2010 | 
    
      | country | city |  |  |  |  |  |  | 
  
  
    
      | Egypt | Cairo | 22.006500 | 22.050000 | 22.361000 | 22.644500 | 22.625000 | 23.718250 | 
    
      | Gizeh | 22.006500 | 22.050000 | 22.361000 | 22.644500 | 22.625000 | 23.718250 | 
    
      | Ethiopia | Addis Abeba | 18.312833 | 18.427083 | 18.142583 | 18.165000 | 18.765333 | 18.298250 | 
    
      | France | Paris | 11.552917 | 11.788500 | 11.750833 | 11.278250 | 11.464083 | 10.409833 | 
    
      | Germany | Berlin | 9.919083 | 10.545333 | 10.883167 | 10.657750 | 10.062500 | 8.606833 | 
    
      | India | Ahmadabad | 26.828083 | 27.282833 | 27.511167 | 27.048500 | 28.095833 | 28.017833 | 
    
      | Bangalore | 25.476500 | 25.418250 | 25.464333 | 25.352583 | 25.725750 | 25.705250 | 
    
      | Bombay | 27.035750 | 27.381500 | 27.634667 | 27.177750 | 27.844500 | 27.765417 | 
    
      | Calcutta | 26.729167 | 26.986250 | 26.584583 | 26.522333 | 27.153250 | 27.288833 | 
    
      | Delhi | 25.716083 | 26.365917 | 26.145667 | 25.675000 | 26.554250 | 26.520250 | 
  
 
.pivot_table().mean()
  - .pivot_table도 dataframe이기에 mean()함수 적용 가능
- .mean()의 옵션으로 axis = ‘columns’/’index’를 입력해 해당 axis에 대한 평균 산출 가능
mean_temp_by_year = temperatures.pivot_table(
    values = "avg_temp_c", 
    index = ["country", "city"], 
    columns = "year").mean()
mean_temp_by_city = temp_by_country_city_vs_year.mean(axis = 'columns')
# Filter for the city that had the lowest mean temp
mean_temp_by_city[mean_temp_by_city == min(mean_temp_by_city)]
country  city  
China    Harbin    4.876551
dtype: float64