Как найти строки в фрейме данных на основе других строк и других фреймов данных

Из вопроса, который я задал здесь, я получил ответ JSON, похожий на это:

(обратите внимание: id в моем образце данных ниже – это числовые строки, но некоторые – буквенно-цифровые)

data=↓**

{
  "state": "active",
  "team_size": 20,
  "teams": {
    "id": "12345679",
    "name": "Good Guys",
    "level": 10,
    "attacks": 4,
    "destruction_percentage": 22.6,
    "members": [
      {
        "id": "1",
        "name": "John",
        "level": 12
      },
      {
        "id": "2",
        "name": "Tom",
        "level": 11,
        "attacks": [
          {
            "attackerTag": "2",
            "defenderTag": "4",
            "damage": 64,
            "order": 7
          }
        ]
      }
    ]
  },
  "opponent": {
    "id": "987654321",
    "name": "Bad Guys",
    "level": 17,
    "attacks": 5,
    "damage": 20.95,
    "members": [
      {
        "id": "3",
        "name": "Betty",
        "level": 17,
        "attacks": [
          {
            "attacker_id": "3",
            "defender_id": "1",
            "damage": 70,
            "order": 1
          },
          {
            "attacker_id": "3",
            "defender_id": "7",
            "damage": 100,
            "order": 11
          }
        ],
        "opponentAttacks": 0,
        "some_useless_data": "Want to ignore, this doesn't show in every record"
      },
      {
        "id": "4",
        "name": "Fred",
        "level": 9,
        "attacks": [
          {
            "attacker_id": "4",
            "defender_id": "9",
            "damage": 70,
            "order": 4
          }
        ],
        "opponentAttacks": 0
      }
    ]
  }
}

Я загрузил это, используя:

df = json_normalize([data['team'], data['opponent']],
                     'members',
                     ['id', 'name'],
                     meta_prefix='team.',
                     errors='ignore')
print(df.iloc(1))
attacks              [{'damage': 70, 'order': 4, 'defender_id': '9'...
id                                                                   4
level                                                                9
name                                                              Fred
opponentAttacks                                                      0
some_useless_data                                                  NaN
team.name                                                     Bad Guys
team.id                                                      987654321
Name: 3, dtype: object

По сути, у меня вопрос из 3 частей.

  1. Как получить строку, подобную приведенной выше, с помощью тега участника? Я пробовал:

    member = df[df['id']=="1"].iloc[0]
    #Now this works, but am I correctly doing this?
    #It just feels weird is all.
    
  2. Как мне получить защиту участника, основываясь только на том, что записываются только атаки, а не защиты (даже несмотря на то, что задан defer_id)? Я пытался:

    df.where(df['tag']==df['attacks'].str.get('defender_id'), df['attacks'], axis=0)
    #This is totally not working.. Where am I going wrong?
    
  3. Поскольку я получаю новые данные из API, мне нужно сравнить старые данные в моей базе данных, чтобы увидеть, есть ли какие-либо новые атаки. Затем я могу просмотреть новые атаки и показать пользователю информацию об атаке.

    Честно говоря, я не могу этого понять, я пытался изучить этот вопрос и этот также, который, как я чувствовал, был близок к тому, что мне нужно, и у меня все еще есть проблемы с обдумыванием концепции. По сути, моя логика такова:

    def get_new_attacks(old_data, new_data)
        '''params
             old_data: Dataframe loaded from JSON in database
             new_data: Dataframe loaded from JSON API response
                       hopefully having new attacks
           returns:
             iterator over the new attacks
        '''
    
        #calculate a dataframe with new attacks listed
        return df.iterrows()
    

Я знаю, что вышеприведенная функция почти не требует усилий, кроме документов, которые я дал (в основном, чтобы показать желаемый ввод/вывод), но поверьте мне, я больше всего ломал голову над этой частью. Я изучал merg все атаки, а затем выполнял reset_index(), и это просто вызывает ошибку из-за того, что атаки представляют собой список. Функция map() во втором вопросе, который я связал выше, поставила меня в тупик.


person Jab    schedule 01.02.2019    source источник
comment
Прошел уже час, прошу прощения за расплывчатое название, только что отредактировал. Я не знаю, что там произошло.   -  person Jab    schedule 01.02.2019
comment
Что такое df1? это df.iloc[1]?   -  person U11-Forward    schedule 05.02.2019
comment
Это должно было быть df. Мой плохой, и я провел много исследований о том, как использовать pandas. Я нашел свой ответ, и @a_guest ответил правильно.   -  person Jab    schedule 05.02.2019
comment
Да, я тоже был готов это сказать :-)   -  person U11-Forward    schedule 07.02.2019


Ответы (1)


Ссылаясь на ваши вопросы по порядку (код ниже):

  1. Мне кажется, что id — это уникальный индекс данных, поэтому вы можете использовать df.set_index('id'), который позволяет вам получать доступ к данным по идентификатору игрока, например, через df.loc['1'].
  2. Насколько я понимаю ваши данные, все словари, перечисленные в каждом из attacks, являются автономными в том смысле, что соответствующий идентификатор игрока не требуется (поскольку attacker_id или defender_id кажется достаточно для идентификации данных). Поэтому вместо того, чтобы иметь дело со строками, содержащими списки, я рекомендую выгружать эти данные в отдельный фрейм данных, что делает их легко доступными.
  3. Как только вы сохраните attacks в собственном фрейме данных, вы можете просто сравнить индексы, чтобы отфильтровать старые данные.

Вот пример кода, иллюстрирующий различные моменты:

# Question 1.
df.set_index('id', inplace=True)
print(df.loc['1'])  # For example player id 1.

# Question 2 & 3.
attacks = pd.concat(map(
    lambda x: pd.DataFrame.from_dict(x).set_index('order'),  # Is 'order' the right index?
    df['attacks'].dropna()
))

# Question 2.
print(attacks[attacks['defender_id'] == '1'])  # For example defender_id 1.

# Question 3.
old_attacks = attacks.iloc[:2]  # For example.
new_attacks = attacks[~attacks.index.isin(old_attacks.index)]
print(new_attacks)
person a_guest    schedule 01.02.2019
comment
Спасибо, я знаю, что это были вопросы новичка. Я только начинал использовать pandas, и мне было трудно понять его нюансы. За прошедший день провел много исследований и просмотрел видео. Спасибо за отличный ответ! - person Jab; 05.02.2019