Как я могу динамически создавать '&' фильтры различной длины для панд DataFrame - PullRequest
0 голосов
/ 01 сентября 2018

Назначение кода:

На основе входных данных (test_tuple & params), которые могут иметь различную длину, из основного DataFrame (df), сгенерировать отфильтрованный DataFrame (Filter_df). Потенциально возможны сотни комбинаций фильтров.

Причина сообщения:

Я бы хотел сказать, что все здесь работает для получения ожидаемого результата. При этом мне не нравится метод реализации Решения 1, где фиктивный объект DataFrame создается в поле, которое цикл последовательно фильтрует и обновляет. Решение 1 кажется немного запутанным, но мне нужно некоторое руководство для реализации чего-то более краткого.

Запрос:

Есть ли способ использовать filter_t, как показано в решении 2?

filter_t отформатирован правильно, но является строкой. Есть ли метод для создания filter_t, чтобы его можно было использовать, как показано?

Пример ввода:

test_tuple = [('Serial Number', [12345]),
              ('Test Points', ['TestpointA', 'TestpointC']),
              ('Voltage_1', [3.0, 3.3, 3.6, 0.0]),
              ('Temperature Setpoint', [0, 60]),
              ('Slew_1', [200, 400, 800, 1600, 3200, 6400])]
params = ['sn', 'tp', 'v1', 'temp', 'slew']

Код:

for i in itertools.product(*[b for _, b in test_tuple]):
     print('\n'.join(f'{a}:{b}' for a, b in zip(params, i)))
     name_params = '_'.join(f'{b}{a}' for a, b in zip(params, i)) 
     filter_t = ' & '.join(f'(self.df["{c[0]}"] == {b})' for b, c in zip(i, test_tuple))
     print(f'filter_t: {filter_t}')
     filter_l = [(c[0], b) for b, c in zip(i, test_tuple)]

Формат filter_t в виде строки:

filter_t: (self.df["Test Points"] == 3P3V) & (self.df["Slew_1"] == 5000)
filter_t: (self.df["Serial Number"] == 2450) & (self.df["Test Points"] == 3P3V) & (self.df["Voltage_1"] == 11.6) & (self.df["Temperature Setpoint"] == 25.0) & (self.df["Slew_1"] == 5000)

Формат filter_l:

filter_l: [('Test Points', '3P3V_Edge'), ('Slew_1', 200)]
filter_l: [('Serial Number', 1234), ('Test Points', '3P3V'), ('Voltage_1', 11.6), ('Temperature Setpoint', 25.0), ('Slew_1', 200)]

Решение 1 - Работает без проблем:

filtered_df = {1: df}
for x in filter_l:
     filtered_df[1] = (filtered_df[1].loc[(filtered_df[1][f'{x[0]}'] == x[1])])

Решение 2 - Возможно?:

df_filter = self.df.loc[filter_t]

Пример фильтра:

df_filter = self.df.loc[(self.df['Serial Number'] == 1234) &
                        (self.df['Test Points'] == '3P3V') &
                        (self.df['Voltage_1'] == 11.6) &
                        (self.df['Temperature Setpoint'] == 25.0) &
                        (self.df['Slew'] == 200)]

Окончательное решение @ Джон Цвинк - Спасибо:

filter_t = ' & '.join(f'{c[0]} == "{b}"' for b, c in zip(i, test_tuple))
filtered_df = df.loc[df.eval(filter_t)]

print(f'filter_t: {filter_t}')
>>> filter_t: Test_Points == "3P3V" & Slew_1 == "5000"

Заранее спасибо за предложения.

1 Ответ

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

Если вы можете установить numexpr, я предлагаю попробовать DataFrame.query () для этого.

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

filter_str = 'Serial_Number == 2450 and Test_Points == "3P3V" and Voltage_1 == 11.6'
df_filter = df.query(filter_str)

Если у вас установлено numexpr, это может быть самое быстрое решение.

Либо создайте список масок, а затем объедините их. Это похоже на ваше решение 1, но лучше его:

masks = [df[x[0]] == x[1] for x in filter_l] # list of bool arrays
filtered_df = df[np.logical_and.reduce(masks)] # combine and apply masks
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...