Python-панды, добавляющие данные в следующий столбец в зависимости от условий - PullRequest
0 голосов
/ 16 сентября 2018

& # x200B;

Я работаю над фреймом данных, содержащим около 200 тыс. Записей, которые выглядят следующим образом (информация заменена случайным текстом):

ID                Description         
1                 Eg.1
2                 Desc.2
3                 Desc.3
80                 
aaa
output
500                
c                   
d
e
f
input
100              Desc.100
200              Desc.200

Я настроил его на пандас-фрейм и думал, что смогу сделать что-то вроде:

for x in df['ID'] :
    if type(df['ID'][x]) == str:
        df['Description'][x-1] += ' ' + df['ID'][x].values       

Чтобы попытаться добавить ошибочный текст в ID (ниже желаемый результат, который я хочу получить)

ID                Description         
1                 Eg.1
2                 Desc.2
3                 Desc.3
80                aaa output
500               c d e f input         
100               Desc.100

Если в столбце идентификаторов хранятся только цифры, а все описания добавляются к предыдущему правильному идентификатору. (другая проблема заключается в том, что в некоторых случаях число ошибочного текста в идентификаторах варьируется от 1 до 10)

Я немного застрял, поскольку x в приведенном выше коде возвращает строку, которая была найдена в разделе df ['ID'], есть какие-нибудь мысли о том, как это можно сделать относительно быстрым способом через записи с более чем 200k?

Спасибо!

Ответы [ 4 ]

0 голосов
/ 16 сентября 2018

Другой подход может быть таким, как показано ниже: Входные данные:

df = pd.DataFrame({'ID': ['1', '2', '3', '80', 'aaa', 'output', '500', 'c', 'd', 'e', 'f', 'input', '100', '200'],
                   'Description': ['Eg.1', 'Desc.2', 'Desc.3', np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, 'Desc.100', 'Desc.200']})

Логика для обработки фрейма данных для получения желаемого результата:

df['IsDigit'] = df['ID'].str.isdigit()
df['Group'] = df['IsDigit'].ne(df['IsDigit'].shift()).cumsum()
dfG = df[df['IsDigit'] == False].groupby(['Group'])['ID'].apply(lambda x: ' '.join(x))
df = df.drop(df[df['IsDigit'] == False].index)
df.loc[df['Description'].isna(), 'Description'] = df[df['Description'].isna()].apply(lambda x: dfG[x['Group'] + 1], axis=1)
df = df.drop(columns=['IsDigit', 'Group']).set_index('ID')

И выдает результат ниже:

       Description
ID                
1             Eg.1
2           Desc.2
3           Desc.3
80      aaa output
500  c d e f input
100       Desc.100
200       Desc.200

Я надеюсь, что это поможет вам и другим, кто ищет подобное решение.

0 голосов
/ 16 сентября 2018

Вы можете попытаться сохранить только числовое значение в «ID», присвоив описание не числовому идентификатору. После пересылки введите идентификатор примените groupby и присоединитесь к описанию.

df['Description'] = df.apply(lambda x : x['Description'] if x['ID'].isdigit() else x["ID"],1).fillna('')
df['ID'] = df.ID.apply(lambda x:x if x.isdigit() else np.nan).fillna(method='ffill')
df = pd.DataFrame(df.groupby(['ID'],sort=False)['Description'].apply(lambda x: ' '.join(x))).reset_index()

Из:

   ID   Description
0   1   Eg.1
1   2   Desc.2
2   3   Desc.3
3   80  aaa output
4   500 c d e f input
5   100 Desc.100
6   200 Desc.200
0 голосов
/ 16 сентября 2018

Это использует numpy почти исключительно. Это быстрее, чем методы групповых панд, хотя код длиннее. Повторяющиеся числовые значения в столбце идентификатора в порядке (все числовые строки будут возвращены независимо от того, дублированы они или нет, так как код стоит).

import numpy as np
import pandas as pd

df = pd.DataFrame({'ID': ['1', '2', '3', '80', 'aaa',
                           'output', '500', 'c', 'd',
                           'e', 'f', 'input', '100', '200'],
                   'Description': ['Eg.1', 'Desc.2', 'Desc.3',
                                   '', '', '', '', '', '', '',
                                   '', '', 'Desc.100', 'Desc.200']})

IDs = df.ID.values

# numeric test function for ID column
def isnumeric(s):
    try:
        float(s)
        return 1
    except ValueError:
        return 0

# find the rows which are numeric and mark with 1 (vs 0)
nums = np.frompyfunc(isnumeric, 1, 1)(IDs).astype(int)

# make another array, which marks
# str IDs with a 1 (opposite of nums)
strs = 1 - nums

# make arrays to hold shifted arrays of strs and nums
nums_copy = np.empty_like(nums)
strs_copy = np.empty_like(strs)

# make an array of nums shifted fwd 1
nums_copy[0] = 1
nums_copy[1:] = nums[:-1]

# make an array of strs shifted back 1
strs_copy[-1] = 0
strs_copy[:-1] = strs[1:]

# make arrays to detect where str and num
# ID segments begin and end
str_idx = strs + nums_copy
num_idx = nums + strs_copy

# find indexes of start and end of ID str segments
starts = np.where(str_idx == 2)[0]
ends = np.where(str_idx == 0)[0]

# make a continuous array of IDs which
# were marked as strings
txt = IDs[np.where(strs)[0]]
# split that array into string segments which will
# become a combined string row value
txt_arrs = np.split(txt, np.cumsum(ends - starts)[:-1])
# join the string segment arrays
txt_arrs = [' '.join(x) for x in txt_arrs]

# find the row indexes which will contain combined strings
combo_str_locs = np.where(num_idx == 2)[0][:len(txt_arrs)]
# put the combined strings into the Description column
# at the proper indexes
np.put(df.Description, combo_str_locs, txt_arrs)
# slice the original dataframe to retain only numeric
# ID rows
df = df.iloc[np.where(nums == 1)[0]]

# If a new index is desired >> df.reset_index(inplace=True, drop=True) 
0 голосов
/ 16 сентября 2018

Вот идея о том, как сделать это в пандах:

Я прочитал ваш пример из буфера обмена

import pandas as pd
import numpy as np
df = pd.read_clipboard()

Сначала я скопировал строковые индексы в описание, где идентификатор былстрока.Потому что это должно идти в поле описания.Я использую str (x) .isnumeric () для обработки каждой ячейки как строки, даже если это не так.Если некоторые ячейки импортируются как числа, а некоторые как строки, то часть .isnumeric вызовет ошибку в полях с числовым типом.

df.loc[df['ID'].apply(lambda x: not str(x).isnumeric()), 'Description'] = df['ID']

Затем я освободил идентификатор из этих полей.только строки записей

df.loc[df['ID'].apply(lambda x: not str(x).isnumeric()), 'ID'] = np.NaN

Я заполнил теперь пустой идентификатор идентификатором предыдущей строки

df['ID'] = df['ID'].fillna(method='ffill')

Поскольку первая строка каждой из этих групп все еще пуста, я удаляю ее и группуостальные

df_result = df.dropna().groupby('ID', sort=False).aggregate(lambda x: ' '.join(x))

print (df_result)

Что следует учитывать: если поврежденные данные находятся не в кадре данных, а в файле, я, вероятно, напишу код, который проходит через файл построчно и записывает фиксированные строки вфайл исправлений.Это не потребовало бы одновременного хранения 200 тыс. Строк в памяти, что упростило бы процесс, поскольку исправление необходимо выполнить только один раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...