#python #pandas #duplicates
#python #панды #дубликаты
Вопрос:
Предположим, у нас есть фрейм данных с двумя типами данных: float
и ndarray
(форма всегда (2,)
):
data = [
0.1, np.array([1.0, 0.1]), np.array([1.0, 0.1]),
np.array([1.0, 0.1]), 0.1, 0.1, np.array([0.1, 1.0]), 1.0
]
df = pd.DataFrame(
{'A': data,}, index=[0., 1., 2.0, 2.6, 3., 3.2, 3.4, 4.0]
)
x | A
----|-------------
0.0 | 1.0
1.0 | [1.0, 0.1]
2.0 | [1.0, 0.1]
2.6 | [1.0, 0.1]
3.0 | 0.1
3.2 | 0.1
3.4 | [0.1, 1.0]
4.0 | 1.0
Я хотел бы обработать последовательные дубликаты, чтобы:
- Отбрасывайте повторения, если они равны
float
s (сохраняя первое в «группе»); - Изменение каждого элемента в «группе» с использованием всех значений индекса этой группы, если эти элементы являются
ndarray
s.
Ожидаемый результат для данного примера будет примерно таким (здесь я попытался пропорционально разделить диапазон [1., 0.1]
на три области):
x | A
----|-------------
0.0 | 1.0
1.0 | [1.0, 0.55]
2.0 | [0.55, 0.28]
2.6 | [0.28, 0.1]
3.0 | 0.1
3.4 | [0.1, 1.0]
4.0 | 1.0
Для начала я попытался использовать df != df.shift()
для поиска дубликатов, но это вызвало бы ошибку при копировании float
ndarry
и не «группировало» более 2 элементов.
Я также пытался groupby(by=function)
, где функция проверяет dtype элемента, но, похоже groupby
, в этом случае это влияет на индекс.
Очевидно, что я могу перебирать строки и отслеживать повторения, но это не очень элегантно (эффективно).
У вас есть какие-либо предложения?
Комментарии:
1. По какому правилу вы меняете [1.0, 0.1] на [1.0, 0.55] (значения для index == 1.0 )? Тот же вопрос для 2 следующих строк.
2. Ну, здесь это не очень важно (для примера подойдет любое правило). Здесь я пытался пропорционально увеличить
split
диапазон[1.0, 0.1]
.
Ответ №1:
Шаг 1: удаление последовательных равных чисел с плавающей запятой
Чтобы проверить, являются ли 2 элемента строки равными числам с плавающей запятой, определите следующую функцию:
def equalFloats(row):
if (type(row.A).__name__ == 'float') and (type(row.B).__name__ == 'float'):
return row.A == row.B
return False
Затем временно добавьте в df столбец B, содержащий предыдущее значение
в столбце:
df['B'] = df.A.shift()
И чтобы удалить последовательные числа с плавающей запятой в столбце (а также удалить
столбец B), запустите:
df = df[~df.apply(equalFloats, axis=1)][['A']]
В настоящее время df содержит:
A
0.0 0.1
1.0 [1.0, 0.1]
2.0 [1.0, 0.1]
2.6 [1.0, 0.1]
3.0 0.1
3.4 [0.1, 1.0]
4.0 1
4.5 2.1
Чтобы проверить, что последовательные, но разные числа с плавающей запятой не удаляются,
я добавил строку с индексом 4.5 и значением 2.1. Как вы видите, он не был удален.
Шаг 2: преобразование массивов в столбец
Определите другую функцию:
def step2(row):
if type(row.A).__name__ == 'ndarray':
arr = row.A
arr[1] = arr.sum() / row.name
return row
return row
(row.name является значением индекса текущей строки).
Затем примените его:
df = df.apply(step2, axis=1)
Результат:
A
0.0 0.1
1.0 [1.0, 2.1]
2.0 [1.0, 0.775]
2.6 [1.0, 0.5473372781065089]
3.0 0.1
3.4 [0.1, 0.12456747404844293]
4.0 1
4.5 2.1
Если вы хотите, измените формулу на шаге 2 на любую другую по вашему выбору.
Отредактируйте следующие комментарии
Я определил df как:
A
0.0 0.1
1.0 [1.0, 0.1]
2.0 [1.0, 0.1]
2.6 [1.0, 0.1]
3.0 0.1
3.1 0.1
3.2 0.1
3.4 [0.1, 1.0]
4.0 1
4.5 2.1
Он содержит 3 последовательных значения 0.1.
Обратите внимание, что вы не написали о том, сколько последовательных
значений содержит такая «группа».
Обе функции могут быть определены также с помощью isinstance:
def equalFloats(row):
if isinstance(row.A, float) and isinstance(row.B, float):
return row.A == row.B
return False
def step2(row):
if isinstance(row.A, np.ndarray):
arr = row.A
arr[1] = arr.sum() / row.name
return row
return row
Затем после запуска:
df['B'] = df.A.shift()
df = df[~df.apply(equalFloats, axis=1)][['A']]
df = df.apply(step2, axis=1)
Результат:
A
0.0 0.1
1.0 [1.0, 1.1]
2.0 [1.0, 0.55]
2.6 [1.0, 0.4230769230769231]
3.0 0.1
3.4 [0.1, 0.3235294117647059]
4.0 1
4.5 2.1
Как вы можете видеть, из последовательности из 3 значений 0.1 осталось
только первое.
Комментарии:
1. Спасибо за ответ. Мне нравится идея рассматривать разные
dtype
s отдельно. Однако в вашем ответе отсутствует важный момент на шаге 2: совместная обработка последовательных дубликатов, поскольку значения должны зависеть от: 1) количества повторений; 2) значений индекса (или, точнее, диапазона значений индекса).2. Кроме того, почему вы выбрали
.__name__ == 'float'
надisinstance(x, float)
?