<jupyter_start><jupyter_text>키워드

- 1. 버전확인
- 2. 시리즈
- 3. .iloc 인덱서 iloc(행, 열)
- 4. .iloc 인덱서 대신 인덱싱 사용
- 5. type 함수 이용 행/열/데이터 속성 및 클래스 확인
- 6. 인스턴스 관리기준(원본, 사본처리)
- 7. 시리즈도 데이터프레임과 같을까?

 데이터프레임.drop(['컬럼,'컬럼2']) == 행단위로 삭제한다 세로로
 그렇기때문에 axis=1 해서 열기준이라고 명시해야함
 데이터프레임.drop(['컬럼,'컬럼2'],axis=1)<jupyter_code># 판다스 가져오기 및 별칭 설정
import pandas as pd
import numpy as np
# 판다스 버전확인
pd.__version__<jupyter_output><empty_output><jupyter_text>판다스 시리즈
 - 판다스의 시리즈는 1차원 벡터이다.
 - 1차원 벡터이므로 파이썬 리스트를 넣으면 만들 수 있다.
 - 그러나 파이썬 리스트와 달리 단일 자료형만 들어간다.(배열이기 때문에)<jupyter_code># 자료형을 명기하지 않았을 때는 내부적으로 원소 값을 보고 추론해 가장 적절한 자료형을 선택한다.
ser = pd.Series([1,2,3])
print("=====1번 판다스.시리즈 내부 리스트(정수구성) 직접 삽입=====")
print(ser)

a = [1,2,3,4,5]
ser2 = pd.Series(a)
print("=====2번 판다스.시리즈 내부 리스트(정수구성) 변수 삽입=====")
print(ser2)
b = ['1',2,'3',4,'5']
ser3 = pd.Series(a)
print("=====3번 판다스.시리즈 내부 리스트(정수 + 문자열 구성) 변수 삽입=====")
print(ser3)

c = [1,'2',3,'4',5]
ser4 = pd.Series(c)
print("=====4번 판다스.시리즈 내부 리스트(정수 + 문자열 구성) 변수 삽입=====")
print(ser4)

# 결과분석
# ser3은 정수, ser4는 문자의 결과가 나왔다. 무슨기준인걸까?
# 시리즈는 배열이다 == 배열은 단일 타입 즉 인트+문자열이라고해도 단일 타입으로 들어간다.<jupyter_output>=====3번 판다스.시리즈 내부 리스트(정수 + 문자열 구성) 변수 삽입=====
0    1
1    2
2    3
3    4
4    5
dtype: int64
=====4번 판다스.시리즈 내부 리스트(정수 + 문자열 구성) 변수 삽입=====
0    1
1    2
2    3
3    4
4    5
dtype: object<jupyter_text>판다스 시리즈 덧셈 연산
 - 시리즈 객체를 연산자를 통해 연산을 하면 계산된 결과는 !!새로운 시리즈!! 객체로 반환된다.
 - 기존 시리즈의 값을 변경하지 않고 새로운 객체를 만들어서 반환하는 것이 판다스 모듈의 기본이다.
 - 형식 : 시리즈 + 시리즈 혹은 시리즈변수명.add(시리즈변수명2석<jupyter_code>print("===ser 원본===")
print(ser)
print("=== ser + ser 시리즈 덧셈연산===")
print(ser + ser)
print("===ser 원본===")
print(ser)
print("===ser2 원본===")
print(ser2)
print("=== ser + ser2 시리즈 덧셈연산===")
print(ser + ser2)

# 결과분석
#시리즈의 튜플수가 달라도 연산은 가능함.
ser.add(ser3)
# 시리즈.add(시리즈) 함수로도 연산이 가능하다.<jupyter_output><empty_output><jupyter_text>검색과 슬라이싱 처리
 - 파이썬에서 여러원소를 같는 Sequence 자료형은 숫자나 키 값(주로 문자열)을 가지고 검색한다.
 - 파이썬에서 특정 부분을 검색하려면 슬라이싱을 사용한다.
 - 그러나 판다스에서는 행과 열에 별도의 이름인 레이블을 추가할 수 있으며 이를 사용해 인덱싱,슬라이싱이 가능하다.<jupyter_code>a   = [1,2,3]
ser = pd.Series(a)
print("=== 시리즈 ser ===")
print(ser)
df  = pd.DataFrame(ser)
print("=====데이터프레임(시리즈)=====")
df<jupyter_output>=== 시리즈 ser ===
0    1
1    2
2    3
dtype: int64
=====데이터프레임(시리즈)=====<jupyter_text>상위 결과분석
 - 데이터프레임을 만들기위해 리스트 => 시리즈(리스트) => 데이터프레임(시리즈)

 - 데이터 프레임 객체를 만들기 위해 행과 열의 레이블(행과열의 별도의이름)을 지정하지 않았지만 
 - 행의 인덱스 레이블이 있고 열의 레이블 이름도 잇음을 확인 할 수 있다.
 - 이렇게 레이블에 이름을 지정하지 않으면 행과 열의 레이블이 정수형으로 자동으로 지정된다.<jupyter_code># 상위의 데이터 프레임의 열이 0이므로
print(df[0])
# 조회하면 데이터프레임은 2차원이므로 이보다 낮은찾원인 1차원의 시리즈가 조회되는 것을 볼 수 있다.

# 중요 !! 
# 데이터 프레임에서 인덱스 검색 조회의 기준은 항상 열이다. 슬라이싱은 행을 기준으로 하므로 검색기준을 명확히 이해해야함.!!
# 대량의 데이터 분석을 할 때에는 변수, 즉 열단위 계산이 많다.
# 이런 경우 각 변수 단위의 처리를 위해 데이터 프레임의 인덱스 검색 기준은 열로 하는 것이다.

# 결과값을 보면 데이터프레임의 name 속성이 0임을 알 수 있다.<jupyter_output>0    1
1    2
2    3
Name: 0, dtype: int64<jupyter_text>데이터 프레임 객체에서 행을 기준으로 내부 데이터 검색
 - 데이터 프레임 객체에서 행을 기준으로 내부의 데이터를검색하기 위해 별도의 속성인 인덱서(indexer)를 제공하고
 - 인덱스 검색과 슬라이싱을 통해 행 값들을 조회할 수 있는 기능도 제공   
------

 - iloc 인덱서를 이용해서 0을 조회하면
 - 행기준이므로 행의 암묵적 인덱스를 기준으로 읽는다       
------

 - 실제 데이터 프레임의 행의 레이블은
 - 순서와 상관없이 생성해도 되므로
 - 실제 레이블을 검색하는 .loc 속성과
 - 암묵적인 인덱스로 검색하는 .iloc
 - 두가지를 제공한다.        
------

 - 행단위로 검색해도  2차원인 데이터 프레임이 하나의 차원으로 축소된 1차원의 시리즈가 만들어 진다.<jupyter_code>print("=== df 원본 ===")
df
df.iloc[0]
#  iloc 사용했으므로 행검색이다. 그리고  행의 암묵적인 인덱스 기준으로(0,1,2 ... ) 0행 즉 첫번재 행을 가져왔다.
print(df.iloc[1,0])
# 더 차원을 축소해서 데이터를 검색하려면 .iloc 인덱서(indexer)의 행과열에 정수 0을 넣는다.
# 데이터프레임.iloc[행,열]
# df.iloc[1,0] 은 1행 0 열 == 2가 출력된다.a
# 추가로
# df.iloc[0,0] == 1, df.iloc[1,0] == 2, df.iloc[2,0] == 3

for i in range(0, 3):
    print(f'.iloc인덱서 [{i},0]은 {df.iloc[i,0]}') #열은 1개 밖에없기때문에(시리즈) 0밖에 넣을 수 없다.
# 리스트 검색하듯 인덱싱 연산자 역시 사용 가능하다.
for i in range(0, 3):
    print(f'인덱싱 df[0][{i}]은 {df[0][i]}') # 열은 1개 밖에없기때문에(시리즈) 0밖에 넣을 수 없다.
                                             # 주의점은 .iloc 인덱싱과 행과 열이 반대이다.

# .iloc 인덱서를 쓰는 이유는 인덱싱으로 검색하면 인덱스와 시리즈를 같이 출력할 수 없다.
# 데이터프레임 전체를 출력하거나 행 하나밖에 출력할 수 없다.
# 즉 df.iloc[0]의 결과인 >> 0  1  >> 인덱스 / 시리즈0번 인덱스를 출력 할 수 없다는 말이다.
print("데이터프레임 인덱싱[0] == 1 행 출력")
print(df[0])
print()
print("데이터프레임 인덱싱[0][0] == 1 행 1 열 출력")
print(df[0][0])

# 결과 분석
# 인덱싱으로 1행만 출력하고 싶었지만 시리즈(데이터프레임) 전체가 나왔음.
# 1행 1열만 출력은 가능하나. 1행의 인덱스 + value 출력 불가
a = [
    [1,2,3],
    [4,5,6],
    [7,8,9]
]
a[0]<jupyter_output><empty_output><jupyter_text>행과 열에 대한 인덱스 정보관리
 - 판다스의 클리스에 대한 내부 정보에 접근할수 있는 인덱스는 별도의 클래스임.
 - 인덱스는 내부 정보를 별도로 관리하면서 정보를 연결해 특정 원소값과 부분 집합을 처리할 수있는 구조를 구성합니다.
 - 데이터 처리는 넘파이의 클래스로 처리하므로 넘파이 모듈과의 연계성도 그대로 유지한다.<jupyter_code># 하나의 데이터 프레임을 만들 때에는 행(index), 열(columns) 등 기본으로 3개의 매개변수가 있고 이를 전부 인자로 전달한다.
df = pd.DataFrame(
    data=[[0,1,2],[3,4,5]], 
    index=list('ab'),         # 문자열을 리스트에 넣으면 한 문자씩 쪼개짐 == ['a', 'b']
    columns=list('cde'))      # 문자열을 리스트에 넣으면 한 문자씩 쪼개짐 == ['c', 'd', 'e']

df

# 결과 분석
# 데이터 프레임의 생성결과를 확인하면 행의 레이블('a','b')과 열의 레이블('c','d','e')이 지정되고 내부데이터 원소값((0,1,2)(3,4,5))이 data로 전달되었다.<jupyter_output><empty_output><jupyter_text>결과 분석
 - 생성된 데이터 프레임의 결과를 보며 2행 3열의 정보를 가진다는 것을 알 수 있다.
 - 행, 열의 레이블 정보를 관리하는 속성인 index, columns가 어떤 클래스로 만들어 졌는지 확인해야함.
     - ex : type(df.index) , type(df.columns)
 - 내부 데이터(values)를 관리하는 속성의 자료형 확인해야 함
     - ex : type(df.values)
 - 참고로 type 클래스는 데이터 프레임 등 판다스 클래스의 속성을 다양한 클래스로 내부의 정보를 사람들이 편하게 볼 수 있게 보여줌<jupyter_code>print("데이터프레임.인덱스 클래스/속성 확인")
print(type(df.index))

print("데이터프레임.컬럼스 클래스/속성 확인")
print(type(df.columns))

print("데이터프레임.밸류즈 클래스/속성 확인")
print(type(df.values))

# 결과 분석
# 레이블의 관리(index, columns)하는 속성은 index 클래스의 인스턴스이고
# 데이터를 관리(values)하는 속성은 numpy클래스의 ndarray라는 것을 알 수 있다.
# 데이터 프레임에서 행과 축을 기반으로 데이터를 접근하기 위한 속성인 indexer(인덱서)의 속성/클래스
print("인덱서 .iloc의 자료형/클래스/속성")
print(type(df.iloc))

print("인덱서 .loc의 자료형/클래스/속성")
print(type(df.loc))

# 결과 분석
# .iloc, .loc 는 두개가 클래스가 다르다.(_iLocIndexer, _LocIndexer )<jupyter_output>인덱서 .iloc의 자료형/클래스/속성
<class 'pandas.core.indexing._iLocIndexer'>
인덱서 .loc의 자료형/클래스/속성
<class 'pandas.core.indexing._LocIndexer'><jupyter_text>인스턴스 관리기준
 - 판다스 모듈은 파이썬에서 클래스로 인스턴스를 만드는 것과 같다.
 - 그러나 실제 연산을 처리할 때는 파이썬과 달리 내부 원소를 갱신하는 것이아니라 새로운 인스턴스를 만든다.
    - 필요한 경우 기존 인스턴스 내부를 갱신하기 위해 메소드 내부의 인스턴스에 지정해서 처리해야함.
    - 기존 인스턴스 내부를 갱신하기보다 새롭게 만들어서 처리하는것이 더빠르기 때문에 판다스에는 이런 기준이 있음.
 - 인스턴스를 다른 변수에 할당을 할 경우에도 기존 인스턴스의 레퍼런스를 공유해 처리하는 구조이다.
    - 이를 별칭(alias)이라 하고 실제 새로운 사본을 만들고자 한다면 copy 메소드를 사용해 다른 인스턴스를 생성하는 것 이라 명확히 표시해야함.   


 변수를 할당할 경우 항상 별칭으로 사용
 - 데이터 프레임을 하나 만들고 변수에 할당하여 그 내부의 값을 변경하면 원본에 있는 데이터를 갱신한다.
 - 즉, 사본을 만들어서 사용하지 않으면 항상 원본도 같이 수정됨.<jupyter_code>test_list = [
    [1,2,3],
    [4,5,6],
    [7,8,9]
    ]
df     = pd.DataFrame(test_list, columns=list('abc'))
df_cop = df
print("=== df 원본 ===")
print(df)
print("=== df 카피 ===")
print(df_cop)
# 정수 100을 df_cop 데이터프레임의 컬럼 레이블 'a'의 원소들에 치환한다.
df_cop['a'] = 100 
# ---------------------------------------------------------------------
print("=== df 원본 ===")
print(df)
print("=== df 카피 ===")
print(df_cop)

# 결과 분석
# df_cop의 컬럼레이블 'a'의 원소들 값만 변경 하였으나, 원본인 df의 컬럼레이블 'a'의 원소들 역시 같이 바뀌었다.
# 왜 이런 결과가 나온 것인가.?

# 두 개의 변수에 실제 값을 관리하는 속성인 values 안의 base 속성을 확인해 보면 두개의 레퍼런스가 같음을 알 수 있다.
print("=== df원본 values.base ===")
print(df.values.base)
print("=== df사본 values.base ===")
print(df_cop.values.base)
print("=== df원본 values.base은 df사본 values.base인가? ===")
print(df.values.base is df_cop.values.base)
print("id 함수는 각각의 변수별로 할당된 번호를 보여준다.")
print("즉, id값은 변수마다 고유의 값을 가져야하므로 달라야한다.")
print(id(df), id(df_cop))
# id 값이 같은걸로 이 두개의 변수는 같은 것임을 알 수 있다.
help(id)<jupyter_output>Help on built-in function id in module builtins:

id(obj, /)
    Return the identity of an object.
    
    This is guaranteed to be unique among simultaneously existing objects.
    (CPython uses the object's memory address.)<jupyter_text>연산된 결과는 항상 새로운 인스턴스로 생성
 - 위에서 선택 연산자(EX: df_cop['a'] = 100 )를 사용한 경우 기존 객체의 값을 바뀌었지만.
 - 다른 연산자나 메소드 등의 대부분 연산을 수행할 때에는 기존 원본은 변경하지 않고 새로운 사본을 만든다.<jupyter_code>print(df)
df_cop2 = df + 99
# 위와 비슷하게 이번에는 선택연산자 말고 
# df 변소에 있는 데이터 프레임을 가지고 특정 값(99)를 덧셈으로 연산하여 다른 변수 df_cop에 할당 했습니다.

print("=== df 원본 ===")
print(df)
print("=== df 카피 ===")
print(df_cop)
print("=== df 카피2 ===")
print(df_cop2)

# 결과 분석
# 하지만 위의 결과와 다르게 원본의 데이터는 변하지 않았다.
# 왜 이런 결과가 나온 것인가.?

# 두 개의 변수에 실제 값을 관리하는 속성인 values 안의 base 속성을 확인해 보면 두개의 레퍼런스가 다르다는 것을 알 수 있다.
print("=== df원본 values.base ===")
print(df.values.base)
print("=== df사본 values.base ===")
print(df_cop2.values.base)
print("=== df원본 values.base은 df사본2 values.base인가? ===")
print(df.values.base is df_cop2.values.base)

print(id(df), id(df_cop2))
# id 값이 같은걸로 이 두개의 변수는 다른 변수임을 알 수 있다.<jupyter_output>=== df원본 values.base ===
[[100 100 100]
 [  2   5   8]
 [  3   6   9]]
=== df사본 values.base ===
[[199 199 199]
 [101 104 107]
 [102 105 108]]
=== df원본 values.base은 df사본2 values.base인가? ===
False
2083740164208 2083740235376<jupyter_text>- 변수 df와 df_cop2의 values 속성 안 base를 비교하면 false로 나왔습니다.
 - 이유는 덧셈연산 등 대부분 판다스에서 제공하는 연산은 새로운 사본을 만들어 결과를 제공하므로 변수에 할당하면 새로운 객체가 저장되기 때문입니다.
 - 몇가지 예외가 있지만.
 - 주로 인덱스연산자를 통한 할당은 기존 객체에 값을 변경하거나 열을 추가함(원본을 바꿈)<jupyter_code># 참고 선택연산자 사용해도 copy() 함수 사용하면 다른 객체를 생성함
df22 = df.copy()
df22['a'] = 200 
print("=== df 원본 ===")
print(df)
print("=== df 카피 ===")
print(df22)<jupyter_output>=== df 원본 ===
     a  b  c
0  100  2  3
1  100  5  6
2  100  8  9
=== df 카피 ===
     a  b  c
0  200  2  3
1  200  5  6
2  200  8  9<jupyter_text>시리즈는 데이터프레임과 똑같이 처리되는가?<jupyter_code>import numpy as np
a = np.arange(4)
print(a, type(a))
series_test1     = pd.Series(index=list('abcd'), data=np.arange(4))
series_test2     = series_test1
series_test1_cop = series_test1.copy()

print("=== series_test1 ===")
print(series_test1)
print("=== series_test2 ===")
print(series_test2)
print("=== series_test_cop ===")
print(series_test1_cop)
print(" series_test1 == series_test2 ? ")
print( series_test1 is series_test2 )
print(" series_test1 == series_test1_cop ? ")
print( series_test1 is series_test1_cop )
print(" id : series_test1, series_test2, series_test3 ")
print(id(series_test1), id(series_test2),id(series_test1_cop))<jupyter_output>series_test1 == series_test2 ? 
True
 series_test1 == series_test1_cop ? 
False
 id : series_test1, series_test2, series_test3 
2083761226512 2083761226512 2083760618560<jupyter_text>결과 분석
 - 결과만 바로 말하자면 series 역시 dataframe과 똑같이 처리된다.
 - 변수2 = 변수1 
 - 변수1을 변수2에 바로 할당을해서 보면 id값이 같다 (같은 객체이다.)
 - 변수3 = 변수1.copy()
 - copy()함수를 사용해서 할당을하고 id값을 보니 다르다. (다른 객체가 생성되어 할당되었다.)  인덱스 검색과 슬라이싱 처리
 - 파이썬에서의 인덱스 검색/슬라이싱은 대괄호([]) 연산으로 처리한다.
 - 판다스 모듈에는 파이썬에서 제공하는 것 이외에
 - 마스킹(masking)검색, 팬시(fancy)검색 등이 추가되었다.
 - 또 한, 속성인 인덱서(indexer)라는 개념도 추가되어 행 중심으로 검색하는 기능도 지원한다.
 - ** 어떠한 경우라도 대괄호([]) 연산을 이용하려면 실제 __getitem__ 스페셜 메소드가 구현되어 있어야한다. **  판다스의 클래스인 시리즈 / 데이터프레임 안에는 어떻게 스페셜 메소드인 __getitem__이 만들어 져있는지 / 어떻게 작동하는지
 __getitem__ 이해하기
 - 판다스 모듈안의 주요클래스에 __getitem__ 스페셜 메소드는 하나의 함수로 구현되어 있다.
 - 판다스에서는 파이썬처럼 클래스의 인스턴스 매소드가 함수로 유지된다.
 - 실제 인스턴스 개체와 매소드가 바인딩되면 메소드로 실행 된다.<jupyter_code>pd.Series.__getitem__
pd.DataFrame.__getitem__
pd.Index.__getitem__
pd.MultiIndex.__getitem__
# 상위의 결과로 __getitem__은 (self, key)  // self는 매소드이기때문에 가짐 // key 파라메터를 가지는 함수임을 알 수 있다. 
# <function pandas.core.indexes.multi.MultiIndex.__getitem__(self, key)>
# 결과값 제일앞에 function이라고 함수라고 나와 있음.
df = pd.DataFrame(
    data=[
        [1,2,3,4],
        [5,6,7,8]
        ],
    index=list('ab'),
    columns=list('가나다라'))

df
print("input >> df.loc.__getitem__")
print(df.loc.__getitem__)

print("input >> dfi.loc.__getitem__")
print(df.iloc.__getitem__)

# 결과분석
# 클래스에서 조회했을때는 함수였지만(<function ~>)
# 인스턴스(클래스를 변수에 할당)에서 조회하면 메소드(<bound method ~>)로 표시되는 이유는
# 이번에는 인스턴스 메소드로 전환이 되었기 때문이다.
from pprint import pprint
a = [
    [1,2,3,4],
    [5,6,7,8]
]
print("=== 2차원 리스트 a ===")
pprint(a, width=20)
print()
print("=== 판다스.데이터프레임 ===")
df
print("=== input>> df['나'] ===")
print(df['나'])

print("=== input>> df.__getitem__('나') ===")
print(df.__getitem__('나'))

print("=== input>> a[0] ===")
print(a[0])

print("=== input>> a.__getitem__(0) ===")
print(a.__getitem__(0))<jupyter_output>=== input>> df['나'] ===
a    2
b    6
Name: 나, dtype: int64
=== input>> df.__getitem__('나') ===
a    2
b    6
Name: 나, dtype: int64
=== input>> a[0] ===
[1, 2, 3, 4]
=== input>> a.__getitem__(0) ===
[1, 2, 3, 4]<jupyter_text>결과분석
 - 인덱스 검색한 것을 보면 파이썬은 행단위 처리를 기준으로한다.
 - ** 그러나 데이터프레임의 인덱스 검색은 열단위 기준으로 처리한다. **   

 
 - 스페셜메소드(special method)(__getitem__)을 직접 실행해도 같은 결과가 나온다.
     - 즉, 실제 연산자는 모두 스페셜 메소드를 변환해서 작동되는 것임을 알 수 있다.<jupyter_code># 데이터 프레임의 한열을 다른 변수에 저장하면 하나의 시리즈가 만들어 진다.
df_ser = df['나']
print("=== df_ser === ")
print(df_ser)

print(" === input >> df_ser['a'] === ")
print( df_ser['a'] )

print(" === input >> df_ser.__getitem__('a') === ")
print( df_ser.__getitem__('a') )


# 시리즈의 인덱스 검색연산자와 스페셜 메소드를 가지고 처리하는 결과가 같다.<jupyter_output>=== df_ser === 
a    2
b    6
Name: 나, dtype: int64
 === input >> df_ser['a'] === 
2
 === input >> df_ser.__getitem__('a') === 
2<jupyter_text>- 파이썬에서는 하나의 키 값이 인덱스 번호를 기반으로 한다.
 - ** 반면 판다스에서는 시리즈와 데이터프레임의 레이블 이름으로 인덱스 검색이 가능하다. **
 - 특정 원소들의 값이 True일때는 가져오지만 False일때는 가져오지 않고 
 - 없는 값으로 처리하기 위해 NaN(==None == False)으로 처리되면 정수형 자료형도 실수형 자료형으로 자동으로 변환됨<jupyter_code>df_ser.__getitem__(df_ser > 3)


print(" === input >> df_ser[df_ser > 3] === ")
print( df_ser[df_ser > 3] )

print(" === input >> df_ser.__getitem__(df_ser > 3) === ")
print( df_ser.__getitem__(df_ser > 3) )

# 결과 분석
# 조건을 주고 인덱스 검색이 가능하다.
df[ df > 3 ]
# df.__getitem__(df > 3) 
# df[(df > 3) == True]
# 위 3개 다 같은 명령어

# 결과 분석
# 조건을 줘서 인덱스 검색 할 수 있다.
# 조건 ==True에 해당하지않은 False 값들 
# 1,2,3은 NaN으로 대치되고 다른 값들은 자동 실수형으로 변환되었다.<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15487015348803376, 'fraction_numerical': 0.03331635993307631, 'mean_word_length': 3.088016651798989, 'pattern_counts': {'":': 0, '<': 56, '<?xml version=': 0, '>': 102, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '15', 'dir': 'jupyter-structured-clean-dedup', 'id': '541775', 'license': 'MIT', 'n_tokens_mistral': 9798, 'n_tokens_neox': 12485, 'n_words': 1873, 'path': 'Python 3/23.pandas_basic_01.ipynb', 'repo_name': 'ByeonUi-Hyeok/practice', 'size': '13747'}