Обновление столбцов dataframe pandas на основе словаря pandas [duplicate]

Что такое NullPointerException?

Хорошим местом для начала является JavaDocs . Они охватывают это:

Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Выполнение длины null, как если бы это был массив.
  • Доступ или изменение слотов с нулевым значением, как если бы это был массив.
  • Бросать нуль, как если бы это было значение Throwable.

Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.

blockquote>

Также, если вы попытаетесь использовать нулевую ссылку с synchronized, который также выдаст это исключение, за JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение выражения равно null, NullPointerException.
blockquote>

Как это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Возьмем простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Идентифицирует нулевые значения

. Первый шаг - точно определить , значения которого вызывают исключение . Для этого нам нужно выполнить некоторую отладку. Важно научиться читать stacktrace . Это покажет вам, где было выбрано исключение:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выбрано в строке 13 (в методе printString). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, что s имеет значение null, а вызов метода length на него вызывает исключение. Мы видим, что программа перестает бросать исключение, когда s.length() удаляется из метода.

Трассировка, где эти значения взяты из

Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что s передается с printString(name) в методе print(), а this.name - null.

Трассировка, где эти значения должны быть установлены

Где установлен this.name? В методе setName(String). С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. ​​

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName() перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчанию setName может помешать ему установить значение null):

private String name = "";

Либо метод print, либо printString может проверить значение null например:

printString((name == null) ? "" : name);

Или вы можете создать класс, чтобы name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

См. также:

Я все еще не могу найти проблему

Если вы попытались отладить проблему и до сих пор не имеете решения, вы можете отправить вопрос для получения дополнительной справки, но не забудьте включить то, что вы пробовали до сих пор. Как минимум, включите stacktrace в вопрос и отметьте важные номера строк в коде. Также попробуйте сначала упростить код (см. SSCCE ).

158
задан TheChymera 1 December 2013 в 06:58
поделиться

7 ответов

Вы можете использовать .replace. Например:

>>> df = pd.DataFrame({'col2': {0: 'a', 1: 2, 2: np.nan}, 'col1': {0: 'w', 1: 1, 2: 2}})
>>> di = {1: "A", 2: "B"}
>>> df
  col1 col2
0    w    a
1    1    2
2    2  NaN
>>> df.replace({"col1": di})
  col1 col2
0    w    a
1    A    2
2    B  NaN

или непосредственно на Series, то есть df["col1"].replace(di, inplace=True).

162
ответ дан DSM 18 August 2018 в 12:18
поделиться
  • 1
    Это не работает для меня, если, если col```` is tuple. The error info is не может сравнивать типы ndarray (dtype = object) 'и' tuple```` – Pengju Zhao 2 August 2017 в 04:54
  • 2
    Похоже, что это больше не работает вообще , что неудивительно, учитывая, что ответ был 4 года назад. Этот вопрос требует нового ответа, учитывая, насколько общая операция ... – PrestonH 21 November 2017 в 18:01
  • 3
    @PrestonH Он отлично работает для меня. Запуск: '3.6.1 |Anaconda custom (64-bit)| (default, May 11 2017, 13:25:24) [MSC v.1900 64 bit (AMD64)]' – Dan 6 December 2017 в 10:47
  • 4
    Меня устраивает. Но как, если я хочу заменить значения во ВСЕХ столбцах? – famargar 3 January 2018 в 11:52
  • 5
    Единственный метод, который помогал мне в ответах, заключался в том, чтобы сделать прямую замену на Серии. Благодаря! – Dirigo 9 March 2018 в 19:17

Более подход, основанный на использовании pandas, заключается в применении функции замены, как показано ниже:

def multiple_replace(dict, text):
  # Create a regular expression  from the dictionary keys
  regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))

  # For each match, look-up corresponding value in dictionary
  return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text) 

После того, как вы определили функцию, вы можете применить ее к вашему фреймворку данных.

di = {1: "A", 2: "B"}
df['col1'] = df.apply(lambda row: multiple_replace(di, row['col1']), axis=1)
0
ответ дан Amirhos Imani 18 August 2018 в 12:18
поделиться

map может быть намного быстрее, чем replace

Если ваш словарь содержит более двух ключей, использование map может быть намного быстрее, чем replace. Существуют две версии этого подхода, в зависимости от того, исчерпывает ли ваш словарь все возможные значения (а также хотите ли вы заменить несоответствия или оставить их как отсутствующие значения):

Исчерпывающее сопоставление

В этом случае форма очень проста:

df['col1'].map(di)       # note: if the dictionary does not exhaustively map all
                         # entries then non-matched entries are changed to NaNs

Хотя map чаще всего принимает функцию как свой аргумент, она может альтернативно принимать словарь или серию: Документация для Pandas.series.map

Неисчерпывающее сопоставление

Если у вас есть не исчерпывающее отображение и вы хотите сохранить существующие переменные для несоответствий, вы можете добавить fillna:

df['col1'].map(di).fillna(df['col1'])

, как в ответе @ jpp здесь: Заменить значения в серии pandas с помощью словаря эффективно

Тесты

Используя следующие данные с версией pandas 0.23.1:

di = {1: "A", 2: "B", 3: "C", 4: "D", 5: "E", 6: "F", 7: "G", 8: "H" }
df = pd.DataFrame({ 'col1': np.random.choice( range(1,9), 100000 ) })

и тестируя с %timeit, похоже, что map примерно в 10 раз быстрее, чем replace.

Обратите внимание, что ваше ускорение с map будет отличаться в зависимости от ваших данных. Самое большое ускорение, похоже, связано с большими словарями и исчерпывающими заменами. См. Ответ @jpp (см. Выше) для более подробных тестов и обсуждения.

49
ответ дан JohnE 18 August 2018 в 12:18
поделиться
  • 1
    Последний блок кода для этого ответа, конечно, не самый элегантный, но этот ответ заслуживает некоторого кредита. Он на порядок быстрее для больших словарей и не использует всю мою оперативную память. Он переименовал 10 000 строк с использованием словаря, в котором за полминуты было около 9 миллионов записей. Функция df.replace, в то время как аккуратная и полезная для небольших диктонов, потерпела крах после запуска в течение 20 минут или около того. – griffinc 11 May 2017 в 03:29
  • 2
  • 3
    @griffinc Спасибо за отзывы и заметьте, что с тех пор я обновил этот ответ с помощью гораздо более простого способа сделать неисчерпывающий случай (благодаря @jpp) – JohnE 3 July 2018 в 10:40
  • 4
    – StatsScared 27 August 2018 в 18:14
  • 5

Добавляя к этому вопросу, если у вас когда-либо было несколько столбцов для переназначения в фрейме данных данных:

def remap(data,dict_labels):
    """
    This function take in a dictionnary of labels : dict_labels 
    and replace the values (previously labelencode) into the string.

    ex: dict_labels = {{'col1':{1:'A',2:'B'}}

    """
    for field,values in dict_labels.items():
        print("I am remapping %s"%field)
        data.replace({field:values},inplace=True)
    print("DONE")

    return data

Надеюсь, что это может быть полезно кому-то.

Cheers

g2]
2
ответ дан Nico Coallier 18 August 2018 в 12:18
поделиться

В вашем вопросе есть немного двусмысленности. Существует не менее трех двух интерпретаций:

  1. клавиши в di относятся к значениям индекса
  2. , клавиши в di относятся к df['col1'] значениям
  3. ключи в di относятся к местоположению индексов (а не к вопросу OP, но выбрасываются для удовольствия.)

Ниже приведено решение для каждого случая.


Случай 1: Если клавиши di предназначены для обозначения значений индекса, вы можете использовать метод update:

df['col1'].update(pd.Series(di))

Например,

import pandas as pd
import numpy as np

df = pd.DataFrame({'col1':['w', 10, 20],
                   'col2': ['a', 30, np.nan]},
                  index=[1,2,0])
#   col1 col2
# 1    w    a
# 2   10   30
# 0   20  NaN

di = {0: "A", 2: "B"}

# The value at the 0-index is mapped to 'A', the value at the 2-index is mapped to 'B'
df['col1'].update(pd.Series(di))
print(df)

дает

  col1 col2
1    w    a
2    B   30
0    A  NaN

Я изменил значения из вашего исходного сообщения, чтобы было ясно, что делает update. Обратите внимание, что ключи из di связаны с значениями индекса. Порядок значений индекса, т. Е. Индекс местоположения - не имеет значения.


Случай 2: Если клавиши в di относятся к df['col1'], то @DanAllan и @DSM показывают, как достичь этого с помощью replace:

import pandas as pd
import numpy as np

df = pd.DataFrame({'col1':['w', 10, 20],
                   'col2': ['a', 30, np.nan]},
                  index=[1,2,0])
print(df)
#   col1 col2
# 1    w    a
# 2   10   30
# 0   20  NaN

di = {10: "A", 20: "B"}

# The values 10 and 20 are replaced by 'A' and 'B'
df['col1'].replace(di, inplace=True)
print(df)

дает

  col1 col2
1    w    a
2    A   30
0    B  NaN

Обратите внимание, как в этом случае клавиши в di были изменены в соответствии с значениями в df['col1'].


Случай 3: Если клавиши в di относятся к местоположениям индекса, вы можете использовать

df['col1'].put(di.keys(), di.values())

, поскольку

df = pd.DataFrame({'col1':['w', 10, 20],
                   'col2': ['a', 30, np.nan]},
                  index=[1,2,0])
di = {0: "A", 2: "B"}

# The values at the 0 and 2 index locations are replaced by 'A' and 'B'
df['col1'].put(di.keys(), di.values())
print(df)

дает

  col1 col2
1    A    a
2   10   30
0    B  NaN

Здесь первая и третья строки были изменены, поскольку клавиши в di являются 0 ] и 2, которые с индексом на основе индекса Python относятся к первому и третьему местоположениям.

49
ответ дан unutbu 18 August 2018 в 12:18
поделиться
  • 1
    replace одинаково хорош и, возможно, лучшее слово для того, что здесь происходит. – Dan Allan 27 November 2013 в 21:06
  • 2
    @DSM позаботился об этом. – Dan Allan 27 November 2013 в 21:11
  • 3
    Не удаляет ли отображаемый целевой фрейм OP неопределенность? Тем не менее, этот ответ полезен, поэтому +1. – DSM 27 November 2013 в 22:35
  • 4
    @DSM: Ой, вы правы, нет возможности Case3, но я не думаю, что целевая датафрейма OP отличает Case1 от Case2, поскольку значения индекса равны значениям столбца. – unutbu 27 November 2013 в 22:47
  • 5
    @unutbu: ах, точка взята. – DSM 27 November 2013 в 22:59

DSM имеет принятый ответ, но в этом ответе немного ошибочно (я не уверен, что ответ когда-либо работал в готовом виде). Вот пример, который работает с текущей версией pandas (0.23.4 с 8/2018):

import pandas as pd

df = pd.DataFrame({'col1': [1, 2, 2, 3, 1],
            'col2': ['negative', 'positive', 'neutral', 'neutral', 'positive']})

conversion_dict = {'negative': -1, 'neutral': 0, 'positive': 1}
df['converted_column'] = df['col2'].replace(conversion_dict)

print(df.head())

Вы увидите, что это выглядит так:

   col1      col2  converted_column
0     1  negative                -1
1     2  positive                 1
2     2   neutral                 0
3     3   neutral                 0
4     1  positive                 1

Документы для pandas.DataFrame.replace здесь .

0
ответ дан wordsforthewise 18 August 2018 в 12:18
поделиться
0
ответ дан U9-Forward 30 October 2018 в 04:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: