Удаление строк из групп в кадре данных, имеющих минимальное значение в определенном столбце - PullRequest
0 голосов
/ 15 мая 2018

У меня есть набор данных в CSV-файле. Я импортировал его, используя панды в качестве кадра данных. Затем я упорядочиваю свои данные от наименьшего к наибольшему на основе столбца, который я назвал Vf.

Отсюда, что пытается сделать мой код: Сгруппируйте отсортированный кадр данных (DF) в группы по 11. Рассчитайте разницу между соответствующими минимальными и максимальными значениями в каждой группе. Выполните итерацию по каждой группе и проверьте, не превышает ли разница между минимальным и максимальным значением какой-либо из групп более 0,2. Если это условие выполняется для какой-либо из групп, я хочу, чтобы код удалил строку данных, содержащую минимальное значение этой группы, из оригинального кадра данных. Затем снова отсортируйте и сгруппируйте фрейм данных, за исключением этой строки. Повторите каждую группу снова, проверяя разницу между минимальным и максимальным значениями. Он должен продолжать делать это, пока не пройдёт каждую группу и не обнаружит, что ни у одного из них нет разницы между минимальным и максимальным значениями, превышающими 0,2.

Прямо сейчас мой код делает это, насколько я могу судить. Но он не останавливается, когда я этого хочу, то есть, когда все группы имеют разницу между минимальным и максимальным значениями менее 0,2, он не останавливается. Кроме того, в этот момент фактически кажется, что строки добавляются обратно в исходный фрейм данных.

Вот код. Обратите внимание, что я установил, чтобы он проходил только первые две группы в моих данных. Также обратите внимание, что строки в цикле my спрашивают, чтобы он печатал операторы и форму отсортированного информационного кадра. Это все для предоставления информации в целях отладки.

def celling():
import numpy as np
import pandas as pd
Data = input("Name of File: ")
DF = pd.read_csv("Y:\CHRIS\{}.csv".format(Data), skiprows = 20, names = ["Serial","Bin","Ir","Vf"])

def sort(Data):
    SortedDF = Data.sort_values(by='Vf')
    GroupedDF = SortedDF.groupby(np.arange(len(SortedDF))//11)
    GroupMax = GroupedDF["Vf"].max()
    GroupMin = GroupedDF["Vf"].min()
    GroupDiff = GroupMax - GroupMin
    GroupMinIndices = GroupedDF["Vf"].idxmin()
#sort(DF)
    for i in range(2):

        if GroupDiff[i] > 0.2:
            DF = Data[Data.index != GroupMinIndices[i]]
            print("Group {} was bad.".format(i))
            print(SortedDF.shape)
            sort(DF)
        else:
            print("Group {} is good.".format(i))
        print(SortedDF.shape)
sort(DF)

Вот пример того, как выглядит исходный фрейм данных:

        Serial  Bin       Ir     Vf
    0        1  1.0  0.00161  170.7
    1        2  1.0  0.00157  173.3
    2        3  1.0  0.00169  171.0
    3        4  1.0  0.00145  172.7
    4        5  1.0  0.00170  171.4
    5        6  1.0  0.00160  172.6
    6        7  1.0  0.00180  172.4
    7        8  1.0  0.00169  172.1
    8        9  1.0  0.00147  170.9
    9       10  1.0  0.00151  172.3
    10      11  1.0  0.00142  171.8
    11      12  1.0  0.00168  171.5

Вот пример кадра данных, отсортированный по Vf:

    Serial  Bin       Ir     Vf
477    478  1.0  0.00180  170.0
359    360  1.0  0.00139  170.1
247    248  1.0  0.00197  170.1
575    576  1.0  0.00159  170.2
267    268  1.0  0.00178  170.2
178    179  1.0  0.00277  170.3
82      83  1.0  0.00145  170.3
574    575  1.0  0.00162  170.3
97      98  1.0  0.00190  170.3
399    400  1.0  0.00172  170.4
21      22  1.0  0.00166  170.4
67      68  1.0  0.00176  170.5
103    104  1.0  0.00154  170.5
553    554  1.0  0.00169  170.5
533    534  1.0  0.00167  170.5
177    178  1.0  0.00160  170.5
35      36  1.0  0.00150  170.5
186    187  1.0  0.00165  170.5
363    364  1.0  0.00172  170.6
487    488  1.0  0.00159  170.6

Итак, в идеале, что код должен делать здесь, это проверить первые 11 строк этого примера, заметить, что разница между самым большим и самым маленьким значениями в этих 11 больше, чем 0,2, и удалить строку с минимальным значением из этого первого 11, в этом случае это будет строка с серийным номером. 478. Затем он должен перегруппировать данные, где строки 2-12 в приведенном выше примере теперь составляют первую группу из 11. Затем следует заметить, что разница min / max по-прежнему превышает 0,2, и начать все сначала. Ниже должна быть первая группа из 11 из приведенного выше примера после выполнения кода

178    179  1.0  0.00277  170.3
82      83  1.0  0.00145  170.3
574    575  1.0  0.00162  170.3
97      98  1.0  0.00190  170.3
399    400  1.0  0.00172  170.4
21      22  1.0  0.00166  170.4
67      68  1.0  0.00176  170.5
103    104  1.0  0.00154  170.5
553    554  1.0  0.00169  170.5
533    534  1.0  0.00167  170.5
177    178  1.0  0.00160  170.5

А вот результат моего кода в его нынешнем виде:

Group 0 was bad.
(643, 4)
Group 0 was bad.
(642, 4)
Group 0 was bad.
(641, 4)
Group 0 was bad.
(640, 4)
Group 0 was bad.
(639, 4)
Group 0 is good.
(638, 4)
Group 1 was bad.
(638, 4)
Group 0 is good.
(637, 4)
Group 1 was bad.
(637, 4)
Group 0 is good.
(636, 4)
Group 1 was bad.
(636, 4)
Group 0 is good.
(635, 4)
Group 1 was bad.
(635, 4)
Group 0 is good.
(634, 4)
Group 1 was bad.
(634, 4)
Group 0 is good.
(633, 4)
Group 1 is good.
(633, 4)
(634, 4)
(635, 4)
(636, 4)
(637, 4)
(638, 4)
(639, 4)
Group 1 is good.
(639, 4)
(640, 4)
Group 1 is good.
(640, 4)
(641, 4)
Group 1 is good.
(641, 4)
(642, 4)
Group 1 is good.
(642, 4)
(643, 4)
Group 1 is good.
(643, 4)
(643, 4)

Обратите внимание, как он удаляет строки, когда читает, что в группе 0 разница между максимальным и минимальным превышает 0,2. Затем он переходит к группе 1. Когда он читает, что максимальная / минимальная разница в группе 1 больше 0,2, он удаляет строку и возвращается к началу цикла for (я знаю, что это не очень эффективно) , Однако обратите внимание, что теоретически он должен остановиться, как только прочитает, что Группа 0 хороша, а затем что Группа 1 хороша, но это не так. И обратите внимание, что после прочтения они оба хороши, кажется, что они начинают добавлять строки обратно в фрейм данных.

Если бы кто-нибудь мог объяснить, почему мой код делает это, или объяснить, что делает мой код, если он этого не делает, это было бы очень признательно. Обратите внимание, я новичок в Python, так что будьте добры! :)

1 Ответ

0 голосов
/ 15 мая 2018

Это сложная проблема, поэтому давайте начнем с ее повторения.

  1. сортировка данных с использованием столбца Vf
  2. группа подряд 11 строк и найти первую группу, где минимальный-максимальный спред превышает 0,2.
  3. разделить данные на две части, начиная с этой группы, первая часть будет хорошая часть, а вторая часть будет плохая и требует строки, которые будут удалены из его начала.
  4. удалить строки с начала bad , пока разброс между Vf и Vf.shift(-10) не превысит 0,2
  5. повторять шаги (group-split-remove) до тех пор, пока в разбиении не появится пустая поврежденная часть
  6. объедините все хорошие детали, чтобы получить окончательный результат
  7. (необязательно) назначить идентификатор группы для конечного результата

создать образец отсортированного фрейма данных:

df = pd.read_table(io.StringIO("""    Serial  Bin       Ir     Vf
477    478  1.0  0.00180  170.0
359    360  1.0  0.00139  170.1
247    248  1.0  0.00197  170.1
575    576  1.0  0.00159  170.2
267    268  1.0  0.00178  170.2
178    179  1.0  0.00277  170.3
82      83  1.0  0.00145  170.3
574    575  1.0  0.00162  170.3
97      98  1.0  0.00190  170.3
399    400  1.0  0.00172  170.4
21      22  1.0  0.00166  170.4
67      68  1.0  0.00176  170.5
103    104  1.0  0.00154  170.5
553    554  1.0  0.00169  170.5
533    534  1.0  0.00167  170.5
177    178  1.0  0.00160  170.5
35      36  1.0  0.00150  170.5
186    187  1.0  0.00165  170.5
363    364  1.0  0.00172  170.6
487    488  1.0  0.00159  170.6"""), sep='\s+')

Вот вспомогательные функции:

def grouper(frame):
    return np.arange(len(frame)) // 11

def remove(frame): 
    return (
        (frame.Vf.shift(-10).fillna(frame.Vf.max()) - frame.Vf) < 0.2
    ).cumsum() > 0

def split_df(frame): 
    return frame.groupby(
        grouper(frame)
    ).Vf.transform(
        lambda x: (x.max() - x.min()) > 0.2
    ).cumsum() > 0

column.cumsum() > 0, где столбец имеет логический тип, фильтрует все строки после того, как встречается первое истинное значение, включая строку, которая является истинной.

Следующая функция реализует описанную выше рекурсивную логику (с помощью некоторых вспомогательных функций, определенных выше)

def group_split_remove(frame):
    temp = frame[split_df(frame)]
    if len(temp) == 0:
        return frame
    return pd.concat([frame[~split_df(frame)], group_split_remove(temp[remove(temp)])])

Теперь рекурсия в python - не всегда лучшая стратегия, поэтому, если вышеприведенное не достаточно быстро или достигает максимальной глубины рекурсии, переформулируйте ее как цикл while. Но я считаю, что рекурсивная формулировка более читабельна в этом случае

с вашими выборочными (отсортированными) данными, group_split_remove(df) возвращает следующий фрейм данных:

     Serial  Bin       Ir     Vf
178     179  1.0  0.00277  170.3
82       83  1.0  0.00145  170.3
574     575  1.0  0.00162  170.3
97       98  1.0  0.00190  170.3
399     400  1.0  0.00172  170.4
21       22  1.0  0.00166  170.4
67       68  1.0  0.00176  170.5
103     104  1.0  0.00154  170.5
553     554  1.0  0.00169  170.5
533     534  1.0  0.00167  170.5
177     178  1.0  0.00160  170.5
35       36  1.0  0.00150  170.5
186     187  1.0  0.00165  170.5
363     364  1.0  0.00172  170.6
487     488  1.0  0.00159  170.6

Как видите, первые 11 строк точно соответствуют ожидаемому результату.

Последним необязательным шагом будет назначение group_id, что можно сделать следующим образом:

res['group_id'] = grouper(res)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...