Сравнение значений одного столбца pandas dataframe в python (преобразование perl в код python) - PullRequest
1 голос
/ 15 октября 2019

Я пытаюсь преобразовать код perl в python для простого умножения столбцов на некоторые константы. Я создал pandas dataframe с несколькими столбцами значений с плавающей точкой. Вот пример файла component.csv.

      NA  MULT   NOA   NOB  CHARGE   EX
0    8.0   1.0  24.0  24.0     0.0  1.0
1    8.0   1.0  24.0  24.0     0.0  1.0
2    8.0   1.0   6.0   6.0     0.0  1.0
3   20.0   1.0  18.0  18.0     0.0  1.0
4   23.0   1.0  21.0  21.0     0.0  1.0
5   26.0   1.0  24.0  24.0     0.0  1.0
6   11.0   1.0  13.0  13.0     0.0  0.0
7   16.0   1.0  19.0  19.0     1.0  0.0
8    1.0   1.0   4.0   4.0    -1.0  0.0
9   17.0   1.0  23.0  23.0     0.0  0.0
10   1.0   1.0   4.0   4.0     0.0  0.0
11   1.0   1.0   4.0   4.0     0.0  0.0

Исходные параметры:

$A1 = 9.3692400791;
$A2 = 9.4492960287;
$B  = 3.8320915550;
$C  = 9.5936653352;
$D  = 1.8739215238;
$E  = 2.4908584058;

Ожидаемый вывод - один столбец (d5):

Df5
-0.2249
-0.2249
-0.0562
-0.1686
-0.1968
-0.2249
-0.1218
-0.1780
-0.0384
-0.2155
-0.0375
-0.0375

После анализа в кадре данных с помощью:

pd.set_option('precision', 8)
df = pd.read_csv("unscaled_components_delimit.csv", delimiter= ",", header=0)

У меня есть несколько условий для проверки, например, следующий скрипт:

if (df['NA'] > 1).any():
    print(True)
elif (df['NA'] == 1).any():
    print(False)

Однако приведенный выше код,печатает только одно значение True, хотя в столбце с заголовком NA есть несколько значений 1.0, это означает, что оно не передается второму elif. Я использовал функцию any (), и, возможно, следует использовать другую функцию, о которой я сейчас не знаю. Следовательно, кто-то может предложить решение для этого?

Цель состоит в том, чтобы сравнить каждый элемент столбца (заголовок NA) с номером 1 (большим или равным). Затем выполните некоторые действия с другими условиями для других столбцов.

Буду признателен за любую помощь или предложение.

Для большей ясности следующий код содержит окончательный требуемый код со всеми необходимымиусловия:

if (df['NA'] > 1).any():
    if (df['MULT'] == 1).any():
        if ((df['NOB'] != 1).any() or (df['NOB'] ==1).any()):
            d5 = -A1*df['NOB']
        elif((df['NOB'] == 1).any()):
            d5 = -E*df['NOB']
    else:
        d5 = -A2*df['NOB'] - B*(df['NOA']-df['NOB'])

elif (df['NA'] == 1).any():
    if (df['MULT'] == 1).any():
        if ((df['EX'] == 0).any() and (df['NOB'] == 4).any() and (df['CHARGE'] == 0).any()):
            d5 = -A1*df['NOB']
        elif((df['NOB'] != 1).any() or ((df['NOB'] == 1).any() and (df['EX'] != 0).any() )):
            d5 = -C*df['NOB']
        elif((df['NOB'] == 1).any() and (df['EX'] == 0).any()):
            d5 = -E*df['NOB']
    else:
        d5 = -C*df['NOB'] - D*(df['NOA']-df['NOB'])

Исходный код perl следующий ($ nh не требуется, hlc в perl - это d5 в python):

 if ($na > 1) {
                    if ($mult == 1) {
                            if (($nob != 1) || (($nob == 1) && ($nh != 0))) {
                                    $hlc = -$A1 * $nob;
                            }
                            elsif (($nob == 1) && ($nh == 0)) {
                                    $hlc = -$E *$nob;
                            }
                    }
                    else {
                            $hlc = -$A2 * $nob - $B * ($noa - $nob);
                    }
            }

            ### HLC for atomic species ###
            elsif ($na == 1) {
                    if ($mult == 1) {

                            if (($ex == 0) && ($nob == 4) && ($charge == 0)) {
                                    $hlc = -$A1 * $nob;
                            }
                            elsif (($nob != 1) || (($nob == 1) && ($ex != 0))) {
                                    $hlc = -$C * $nob;
                            }
                            elsif (($nob == 1) && ($ex == 0)) {
                                    $hlc = -$E *$nob;
                            }
                    }
                    else {
                             $hlc = -$C * $nob - $D * ($noa - $nob);
                    }
            }

Ответы [ 2 ]

1 голос
/ 15 октября 2019

@ Денвер уже объяснил, почему ваши сравнения в столбце NA не дают ожидаемого результата.

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

# a series of bools, indicating for which index our condition is true
na_gt_1_series = df["NA"] > 1
print(na_gt_1)

# creating a new column based on the values of the NA column
df["na_gt_1"] = na_gt_1_series
print(df)

Теперь, поскольку условия здесь довольно сложные, я думаю, было бы проще использовать функцию применения панд, которая применяет функциювдоль определенной оси фрейма данных.

def get_row_df5(row):
    df5 = 0
    if row["NA"] > 1:
        if row["MULT"] == 1:
            if row["NOB"] == 1:
                df5 = -A1 * row["NOB"]
        else:
            df5 = -A2 * row["NOB"] - B * (row["NOA"] - row["NOB"])
    elif row["NA"] == 1:
        if row["MULT"] == 1:
            if row["EX"] == 0 and row["NOB"] == 4 and row["CHARGE"] == 0:
                df5 = -A1 * row["NOB"]
            elif row["NOB"] != 1 or row["NOB"] == 1 and row["EX"] != 0:
                df5 = -C * row["NOB"]
            elif row["NOB"] == 1 and row["EX"] == 0:
                df5 = -E * row["NOB"]
        else:
            df5 = -C * row["NOB"] - D * (row["NOA"] - row["NOB"])
    return df5

df5_res = df.apply(func=get_row_df5, axis=1)

К сожалению, такая простота обходится дорого. Для DataFrame из 120000 строк, созданного путем дублирования данных в качестве примера, решение для применения занимает ~ 4 с против ~ 40 мс (в 100 раз быстрее) для следующего решения.

def get_df5_broad(df_in):
    na_lt_1 = df_in["NA"] > 1
    na_eq_1 = df_in["NA"] == 1
    mult_eq_1 = df_in["MULT"] == 1
    mult_ne_1 = ~mult_eq_1

    res_series = pd.Series(np.zeros(shape=df_in.shape[0]))

    res_series.loc[na_lt_1 & mult_eq_1 & (df_in["NOB"] == 1)] = -A1 * df_in["NOB"]
    res_series.loc[na_lt_1 & mult_ne_1] = -A2 * df_in["NOB"] - B * (df_in["NOA"] - df_in["NOB"])
    res_series.loc[na_eq_1 & mult_eq_1 & (df_in["EX"] == 0) & (df_in["NOB"] == 4) & (df_in["CHARGE"] == 0)] = -A1 * df_in["NOB"]
    res_series.loc[na_eq_1 & mult_eq_1 & ((df_in["NOB"] != 1) | ((df_in["NOB"] == 1) & (df_in["EX"] != 0)))] = -C * df_in["NOB"]
    res_series.loc[na_eq_1 & mult_eq_1 & (df_in["NOB"] == 1) & (df_in["EX"] == 0)] = -E * df_in["NOB"]
    res_series.loc[na_eq_1 & mult_ne_1] = -C * df_in["NOB"] - D * (df_in["NOA"] - df_in["NOB"])

    return res_series

Наконец, этот следующий методлучшее из обоих миров. По своему дизайну и простоте он похож на метод apply, но только в 5 раз медленнее, чем предыдущая высокопроизводительная версия.

def get_df5_tupe(tupe):
    df5 = 0
    if tupe.NA > 1:
        if tupe.MULT == 1:
            if tupe.NOB == 1:
                df5 = -A1 * tupe.NOB
        else:
            df5 = -A2 * tupe.NOB - B * (tupe.NOA - tupe.NOB)
    elif tupe.NA == 1:
        if tupe.MULT == 1:
            if tupe.EX == 0 and tupe.NOB == 4 and tupe.CHARGE == 0:
                df5 = -A1 * tupe.NOB
            elif tupe.NOB != 1 or tupe.NOB == 1 and tupe.EX != 0:
                df5 = -C * tupe.NOB
            elif tupe.NOB == 1 and tupe.EX == 0:
                df5 = -E * tupe.NOB
        else:
            df5 = -C * tupe.NOB - D * (tupe.NOA - tupe.NOB)
    return df5

def get_df5_iter(df_in):
    return pd.Series((get_df5_tupe(curr) for curr in df_in.itertuples(index=False)))

Примечание: эти методы не всегда возвращают правильный ответ из-за логических неоднозначностей в OP. Я отредактирую свое решение, как только появятся правильные логические выражения.

1 голос
/ 15 октября 2019

Этот блок здесь:

if (df['NA'] > 1).any():
    print(True)
elif (df['NA'] == 1).any():
    print(False)

Всегда будет печатать True, поскольку столбец «NA» имеет значения больше 1 и, следовательно, второй elif не будет оцениваться. Основываясь на ваших комментариях, я думаю, что вы хотите просмотреть цикл данных и оценить каждый элемент, если я не ошибаюсь. Вы можете попробовать что-то вроде этого:

for val in df['NA']:
    if val > 1:
        print(True)
    elif val == 1:
        print(False)

Это будет оценивать каждый элемент столбца «NA», для вашего фактического варианта использования вы захотите узнать индекс данного val, который может быть сделан с помощьюenumerate. Например:

for (idx, val) in enumerate(df['NA']):
    if val > 1:
        d5 = -A1 * df['NOB'].iloc[idx]
    elif val == 1:
        d5 = E * df['NOB'].iloc[idx]

idx - это индекс текущего элемента, и вы можете получить доступ к элементу других столбцов по этому индексу, используя iloc. Надеюсь, это поможет, удачи.

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