Самый эффективный метод для изменения значений в больших фреймах данных - Python - PullRequest
1 голос
/ 07 апреля 2020

Обзор: я работаю с pandas фреймами данных переписи, хотя они имеют только два столбца, их длина составляет несколько сотен тысяч строк. Один столбец - это идентификационный номер блока переписи, а другой - значение «место», которое является уникальным для города, в котором находится этот идентификатор блока переписи.

Пример данных:

    BLOCKID          PLACEFP
0    60014001001000  53000
1    60014001001001  53000
...
5844 60014099004021  53000
5845 60014100001000    
5846 60014100001001
5847 60014100001002  53000     

Проблема: как показано выше, есть несколько пустых значений, хотя у них есть идентификатор блока переписи в соответствующей строке. Я обнаружил, что в нескольких случаях идентификатор блока переписи, в котором отсутствует значение места, расположен в том же городе, что и окружающие блоки, у которых нет значения отсутствующего места, особенно если значения мест размещения совпадают - как показано выше, с индексами с 5844 по 5847 - эти два блока расположены в той же общей области, что и окружающие блоки, но, похоже, просто не хватает значения места.

Цель: я хочу иметь возможность go через этот фрейм данных найдите эти экземпляры и заполните значение отсутствующего места на основе значения места перед отсутствующим значением и значения места, следующего непосредственно за ним.

Текущее состояние и препятствие: я написал al oop, который проходит через фрейм данных, чтобы исправить эти проблемы, как показано ниже.

current_state_blockid_df = pandas.DataFrame({'BLOCKID':[60014099004021,60014100001000,60014100001001,60014100001002,60014301012019,60014301013000,60014301013001,60014301013002,60014301013003,60014301013004,60014301013005,60014301013006], 
'PLACEFP': [53000,,,53000,11964,'','','','','','',11964]})

for i in current_state_blockid_df.index:
    if current_state_blockid_df.loc[i, 'PLACEFP'] == '':
        #Get value before blank
        prior_place_fp = current_state_blockid_df.loc[i - 1, 'PLACEFP']
        next_place_fp = ''
        _n = 1

        # Find the end of the blank section
        while next_place_fp == '':
            next_place_fp = current_state_blockid_df.loc[i + _n, 'PLACEFP']
            if next_place_fp == '':
                _n += 1

        # if the blanks could likely be in the same city, assign them the city's place value
        if prior_place_fp == next_place_fp:
            for _i in range(1, _n):
                current_state_blockid_df.loc[_i, 'PLACEFP'] = prior_place_fp

Однако, как и ожидалось, он очень медленный при работе с сотнями тысяч или строк данных. Я подумал об использовании, возможно, ThreadPool executor, чтобы разделить работу, но я не совсем понял логи c, которые я бы использовал для этого. Одна возможность немного ускорить его - это снять проверку, чтобы увидеть, где находится конец промежутка, и вместо этого просто заполнить его тем, что было предыдущим значением места перед пробелами. Хотя это может оказаться моим переходом, все же есть вероятность, что он слишком медленный, и в идеале я бы хотел, чтобы он заполнялся только при совпадении значений до и после, исключая возможность ошибочного назначения блока. Если у кого-то есть другое предложение относительно того, как этого можно достичь быстро, оно будет очень признательно.

Ответы [ 2 ]

2 голосов
/ 07 апреля 2020

Вы можете использовать shift, чтобы ускорить процесс. Тем не менее, это не помогает в случаях, когда в строке несколько пробелов.

df['PLACEFP_PRIOR'] = df['PLACEFP'].shift(1) 
df['PLACEFP_SUBS'] = df['PLACEFP'].shift(-1)

criteria1 = df['PLACEFP'].isnull()
criteria2 = df['PLACEFP_PRIOR'] == df['PLACEFP_AFTER']
df.loc[criteria1 & criteria2, 'PLACEFP'] = df.loc[criteria1 & criteria2, 'PLACEFP_PRIOR']

Если вам в конечном итоге придется перебирать кадр данных, используйте df.itertuples. Вы можете получить доступ к значениям столбца в строке с помощью точечной нотации (row.column_name).

for idx, row in df.itertuples():
    # logic goes here

1 голос
/ 08 апреля 2020

Используя ваш фрейм данных, как определено

def fix_df(current_state_blockid_df):
    df_with_blanks = current_state_blockid_df[current_state_blockid_df['PLACEFP'] == '']
    df_no_blanks = current_state_blockid_df[current_state_blockid_df['PLACEFP'] != '']
    sections = {}
    last_i = 0
    grouping = []

    for i in df_with_blanks.index:
        if i - 1 == last_i:
            grouping.append(i)
            last_i = i
        else:
            last_i = i
            if len(grouping) > 0:
                sections[min(grouping)] = {'indexes': grouping}

            grouping = []
            grouping.append(i)

    if len(grouping) > 0:
        sections[min(grouping)] = {'indexes': grouping}

    for i in sections.keys():
        sections[i]['place'] = current_state_blockid_df.loc[i-1, 'PLACEFP']

    l = []

    for i in sections:
        for x in sections[i]['indexes']:
            l.append(sections[i]['place'])

    df_with_blanks['PLACEFP'] = l
    final_df = pandas.concat([df_with_blanks, df_no_blanks]).sort_index(axis=0)
    return final_df

df = fix_df(current_state_blockid_df)
print(df)

Вывод:

     BLOCKID PLACEFP
0   60014099004021   53000
1   60014100001000   53000
2   60014100001001   53000
3   60014100001002   53000
4   60014301012019   11964
5   60014301013000   11964
6   60014301013001   11964
7   60014301013002   11964
8   60014301013003   11964
9   60014301013004   11964
10  60014301013005   11964
11  60014301013006   11964
...