Проблема
У меня есть набор фреймов данных, где каждая строка должна иметь уникальное значение идентификатора, но иногда импортированные данные содержат несколько строк с одним и тем же идентификатором. Я хочу сделать groupby
операция или что-то подобное, которое проверяет каждую группу идентификаторов на наличие специальных символов, указывающих «нет данных», проверяет, что каждый столбец группы имеет ровно одно неспециальное значение (которое может повторяться в нескольких строках), а затем сворачивает группы идентификаторов в одну строку, значения столбцов которой являются либо уникальными неспециальными значениями столбцов из группы, либо специальными значениями в столбцах, где ни одна строка в группе не содержит данных.
Я хочу, чтобы код вызывал ошибку, если в столбце группы есть несколько разных неспециальных значений. Итак, если метка столбца идентификатора 'id'
и специальные символы находятся в 'x?'
тогда col2
ниже должно возникнуть исключение:
id src col1 col2 col3
0 f x 5 m 1
1 f a 5 n ?
Я собрал следующее решение, но я думаю, что должен быть способ попроще. Мой главный вопрос заключается в том, могу ли я сделать это с помощью большего количества нативного кода pandas вместо использования нескольких вспомогательных функций, как я сделал ниже.
Мой код
В настоящее время я выполняю это с помощью функции, называемой merge_rows
который использует три вспомогательные функции:
get_nonspecial_values
получает все неспециальные значения в столбцеcheck_column_consistency
проверяет, имеет ли столбец ровно одно неспециальное значениеget_inconsistent_ids
получает (надеюсь, пустой) набор значений идентификатора, строки которых конфликтуют друг с другом, например, со значением идентификатора'f'
выше.
Я думаю, что последние две строчки в get_inconsistent_ids
и merge_rows
вероятно, можно было бы выполнить с помощью встроенной операции pandas, которая затем сделала бы кучу кода, который я написал, ненужным, но я не могу понять, что это за операция (я также не уверен, что это строки для обратите внимание при пересмотре этого кода).
import pandas as pd
def get_nonspecial_values(series, specials):
'''
takes a series representing a column and returns all
values in the column that are not in specials
'''
counts = series.value_counts()
return [v for v in counts.index if str(v) not in specials]
def check_column_consistency(series, specials):
'''
takes a series representing a column and returns True
if there is exactly one value that is not in specials
'''
nonspecial = get_nonspecial_values(series, specials)
if len(nonspecial) not in [1, 0]:
return False
else:
return True
def get_inconsistent_ids(df, specials, id_column):
'''
takes a dataframe, special values, and a column label
returns all ID values that represent a set of rows in which
at least one column has multiple different nonspecial values
'''
where_nonunique_ids = df[id_column].duplicated(keep=False)
if not any(where_nonunique_ids):
return []
else:
rows_with_nonunique_ids = df.loc[where_nonunique_ids]
check_group = lambda x: all(x.apply(check_column_consistency, args=(specials,)))
where_ids_consistent = rows_with_nonunique_ids.groupby(id_column).apply(check_group)
return [v for v in where_ids_consistent.index if not where_ids_consistent[v]]
def merge_rows(df, specials="", id_column=''):
'''
performs the merge operation described above
'''
inconsistent_ids = get_inconsistent_ids(df, specials=specials, id_column=id_column)
if len(inconsistent_ids) > 0:
raise ValueError('found the following ID values with inconsistent rows', inconsistent_ids)
rows_with_nonunique_ids = df.loc[df[id_column].duplicated(keep=False)]
unique_column_value = lambda x: (get_nonspecial_values(x, specials) or ['x'])[0]
get_group_values = lambda x: x.apply(unique_column_value)
values_by_id = rows_with_nonunique_ids.groupby(id_column).apply(get_group_values)
return pd.DataFrame(data=values_by_id.values, columns=df.columns)
df = pd.DataFrame({'id' : ['a','b','c','c','d','d','d'],
'src': ['x','a','a','a','a','a','a'],
'col1':[ 5, 5, '?','?', 5, 5, 'x'],
'col2':['x','n','n','x','n','x','x'],
'col3':[ 1 , 3 ,'?', 9 ,'?', 8 ,'x']})
print(merge_rows(df, specials="x?", id_column='id'))
Приведенный выше код дает мне следующий вывод
id src col1 col2 col3
0 c a x n 9
1 d a 5 n 8