programing

반복 행 루프에서 판다 데이터 프레임의 값 업데이트

javajsp 2023. 5. 4. 18:40

반복 행 루프에서 판다 데이터 프레임의 값 업데이트

나는 내가 사용한 지오코딩 작업을 하고 있습니다.selenium위치의 주소에 필요한 x-y 좌표를 스크랩하기 위해 xls 파일을 팬더 데이터 프레임으로 가져왔으며 아래와 같이 명시적 루프를 사용하여 x-y 좌표가 없는 행을 업데이트하려고 합니다.

for index, row in rche_df.iterrows():
    if isinstance(row.wgs1984_latitude, float):
        row = row.copy()
        target = row.address_chi
        dict_temp = geocoding(target)
        row.wgs1984_latitude = dict_temp['lat']
        row.wgs1984_longitude = dict_temp['long']

판다 데이터 프레임 위로 이동한 후 이 기능이 "걸리지 않는 이유는 무엇입니까?"를 읽었습니다.그리고 그것을 충분히 알고 있습니다.iterrows편집을 위해 복사본이 아닌 보기만 제공합니다. 하지만 값 행을 한 행씩 업데이트하려면 어떻게 해야 합니까?아이즈lambda실현 가능합니까?

반환되는 행iterrows복사본은 원본 데이터 프레임에 더 이상 연결되지 않으므로 편집해도 데이터 프레임이 변경되지 않습니다.고맙게도, 당신이 받은 각각의 물건들은iterrows에는 현재 인덱스가 포함되어 있으며, 이 인덱스를 사용하여 데이터 프레임의 관련 행을 액세스하고 편집할 수 있습니다.

for index, row in rche_df.iterrows():
    if isinstance(row.wgs1984_latitude, float):
        row = row.copy()
        target = row.address_chi        
        dict_temp = geocoding(target)
        rche_df.loc[index, 'wgs1984_latitude'] = dict_temp['lat']
        rche_df.loc[index, 'wgs1984_longitude'] = dict_temp['long']

제 경험상, 이 접근법은 다음과 같은 접근법을 사용하는 것보다 더 느린 것 같습니다.apply또는map그러나 항상 그렇듯이 코딩의 성능과 성능을 절충하는 방법은 사용자에게 달려 있습니다.

질문을 기반으로 한 또 다른 방법:

for index, row in rche_df.iterrows():
    if isinstance(row.wgs1984_latitude, float):
        row = row.copy()
        target = row.address_chi        
        dict_temp = geocoding(target)
        
        rche_df.at[index, 'wgs1984_latitude'] = dict_temp['lat']
        rche_df.at[index, 'wgs1984_longitude'] = dict_temp['long']

링크는 다음과 같은 차이점을 설명합니다..loc그리고..at.곧,.at보다 빠른..loc.

사용itertuples()대신

Pandas DataFrames는 실제로 열/시리즈 객체의 모음입니다(예:for x in df열 레이블을 통해 반복됩니다. 따라서 구현할 루프가 있더라도 여러 열에 걸쳐 루프가 반복되는 것이 좋습니다. iterrows()는 각 행에 대해 Series를 생성하여 코드 속도가 매우 느려지기 때문에 "원본" 판다 동작에 대한 반패턴입니다.더 나은/더 빠른 옵션은 를 사용하는 것입니다.itertuples()인덱스 또는 열 레이블로 액세스할 수 있는 각 행의 명명된 튜플을 만듭니다.OP에서 코드를 적용하기 위한 수정은 거의 없습니다.

또한 (@Alireza Mazochi가 언급했듯이) 단일 셀에 값을 할당하려면,at보다 빠름loc.

for row in rche_df.itertuples():
#                  ^^^^^^^^^^   <------ `itertuples` instead of `iterrows`
    if isinstance(row.wgs1984_latitude, float):
        target = row.address_chi
        dict_temp = geocoding(target)
        rche_df.at[row.Index, 'wgs1984_latitude'] = dict_temp['lat']
        rche_df.at[row.Index, 'wgs1984_longitude'] = dict_temp['long']
        #       ^^ ^^^^^^^^^  <---- `at` instead of `loc` for faster assignment
        #                           `row.Index` is the row's index, can also use `row[0]`

보다시피, 사용itertuples()를 사용하는 것과 구문이 거의 같습니다.iterrows()하지만 6배 이상 빠릅니다(간단한 방법으로 확인할 수 있습니다).timeit테스트)를 수행합니다.

2. to_dict()또한 옵션입니다.

의 한 가지 itertuples()열 레이블에 즉, 열블에공있예을때마다이백레이예(때▁there마▁in다▁whenever▁is:▁that▁space있:'Col A'튜플로 를 들어 등), 명된튜변때것망로이, 를들어므예질가플환될명로들,'address_chi'이었다'address chi'다음을 통해 액세스할 수 없습니다.row.address chi이 문제를 해결하는 한 가지 방법은 데이터 프레임을 사전으로 변환하고 반복하는 것입니다.

다시 말하지만, 구문은 다음에 사용된 구문과 거의 같습니다.iterrows().

for index, row in rche_df.to_dict('index').items():
#                         ^^^^^^^^^^^^^^^^^^^^^^^^  <---- convert to a dict
    if isinstance(row['wgs1984_latitude'], float):
        target = row['address_chi']
        dict_temp = geocoding(target)
        rche_df.at[index, 'wgs1984_latitude'] = dict_temp['lat']
        rche_df.at[index, 'wgs1984_longitude'] = dict_temp['long']

이방배또약더 6빠니다릅보다 약 더 .iterrows()하지만 보다 약간 느립니다.itertuples() (보다 메모리 입니다.)itertuples() 사전을 반면에 왜하면그명사만전반을드면는에인시적것은냐▁because▁it.itertuples()생성기를 생성합니다.

필요한 열/행에만 반복

필요한 의 주요 가 OP 특 코 그 주 목 병 은 상 현 요 의 프 임 서 기 다 루 프 가 니 입 이 능 유 필 때 한 요 로 때 에 레 리 고 정 드 일 으 적 반 터 이 데 로 다 판 의 ▁the ▁that ▁the 그 ▁op ▁in and ck ▁function ▁in ▁the ▁datageocoding()벡터화되지 않았습니다.더 한 방법은 입니다. ( 라서코드훨빠씬만따한가방관다것입코니호열드는출하만를에서련법지은드는를르게▁(▁so따다것▁much입니▁it▁column▁on▁code▁relevant▁to▁the호코▁only▁call출는드하▁is▁the▁to▁make)'address_chi' 및 행마스크를 됨). 및 관련 행(부울 마스크를 사용하여 표시됨).

부울 마스크를 만드는 것은 원래 코드에 if-절이 있었기 때문에 필요했습니다.하지 않은 하지 않으므로 한 루프는 특정됩니다.address_chi).

# boolean mask to filter only the relevant rows
# this is analogous to if-clause in the loop in the OP
msk = [isinstance(row, float) for row in rche_df['wgs1984_latitude'].tolist()]

# call geocoding on the relevant values 
# (filtered using the boolean mask built above) 
# in the address_chi column
# and create a nested list
out = []
for target in rche_df.loc[msk, 'address_chi'].tolist():
    dict_temp = geocoding(target)
    out.append([dict_temp['lat'], dict_temp['long']])

# assign the nested list to the relevant rows of the original frame
rche_df.loc[msk, ['wgs1984_latitude', 'wgs1984_longitude']] = out

은 이방배보약 40다빠보다 약 .iterrows().


작업 예제 및 성능 테스트

def geocoding(x):
    return {'lat': x*2, 'long': x*2}


def iterrows_(df):
    
    for index, row in df.iterrows():
        if isinstance(row.wgs1984_latitude, float):
            target = row.address_chi        
            dict_temp = geocoding(target)
            df.at[index, 'wgs1984_latitude'] = dict_temp['lat']
            df.at[index, 'wgs1984_longitude'] = dict_temp['long']
    
    return df


def itertuples_(df):
    
    for row in df.itertuples():
        if isinstance(row.wgs1984_latitude, float):
            target = row.address_chi
            dict_temp = geocoding(target)
            df.at[row.Index, 'wgs1984_latitude'] = dict_temp['lat']
            df.at[row.Index, 'wgs1984_longitude'] = dict_temp['long']
        
    return df


def to_dict_(df):
    
    for index, row in df.to_dict('index').items():
        if isinstance(row['wgs1984_latitude'], float):
            target = row['address_chi']
            dict_temp = geocoding(target)
            df.at[index, 'wgs1984_latitude'] = dict_temp['lat']
            df.at[index, 'wgs1984_longitude'] = dict_temp['long']
            
    return df


def boolean_mask_loop(df):

    msk = [isinstance(row, float) for row in df['wgs1984_latitude'].tolist()]

    out = []
    for target in df.loc[msk, 'address_chi'].tolist():
        dict_temp = geocoding(target)
        out.append([dict_temp['lat'], dict_temp['long']])

    df.loc[msk, ['wgs1984_latitude', 'wgs1984_longitude']] = out
    
    return df


df = pd.DataFrame({'address_chi': range(20000)})
df['wgs1984_latitude'] = pd.Series([x if x%2 else float('nan') for x in df['address_chi'].tolist()], dtype=object)


%timeit itertuples_(df.copy())
# 248 ms ± 12.6 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit boolean_mask_loop(df.copy())
# 38.7 ms ± 1.19 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit to_dict_(df.copy())
# 289 ms ± 10.1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit iterrows_(df.copy())
# 1.57 s ± 27.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

언급URL : https://stackoverflow.com/questions/25478528/updating-a-value-in-a-pandas-dataframe-in-an-iterrows-loop