# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     name: python3
# ---

# + [markdown] id="8yI7r1V4K43j"
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# + [markdown] id="uRb_t3yvvZe9"
# # Google I/O 2022: Product Fairness Testing for Developers
#
# ---
# **Note: if you want to run this notebook, you need to clone it: use *__"File" -> "Save a copy in Drive"__* to get your own copy that you can play with.**
#
#
#
#

# + [markdown] id="tTxo-SRaNhRP"
# # Content
#
# This notebook was created for Google I/O workshop on product fairness testing.
#
# ## [1. Load and explore examples in our dataset](#scrollTo=kKQAvjrvNnWO)
#
#
# ## [2. Filter data using the sensitive terms dataset](#scrollTo=mKwIfGoFN_qK)
#
#
# ## [3. Refine our analysis using interactions](#scrollTo=jPHSuAfCUCkk)

# + [markdown] id="kKQAvjrvNnWO"
# # 1. Load and explore examples in our dataset
# Let's load some example data to analyze. These examples are a sample of about 1.1k conversations from WikiDialog, a dataset of 11 million generated conversations.

# + id="n4adJk13u9e5"
#@title Import relevant libraries 

# These are standard Python libraries useful to load and filter data.
import re
import csv
import collections
import io
import logging
import random

# Pandas and data_table to represent the data and view it nicely. 
import pandas as pd
from google.colab import data_table

# The datasets
wiki_dialog_url = 'https://raw.githubusercontent.com/google/responsible-innovation/main/data/wiki_dialog.csv'
sensitive_terms_url = 'https://raw.githubusercontent.com/google/responsible-innovation/main/data/sensitive_terms.csv'
interaction_table_url = 'https://raw.githubusercontent.com/google/responsible-innovation/main/data/interaction_table.csv'


# + id="yeUT1W-VvHvS"
#@title Load data

# functions

def view(df, include_index=False):
  """Display a Pandas data frame as an easy to use data table."""
  view_table = data_table.DataTable(df, include_index=include_index,
                                    max_columns=100, num_rows_per_page=10)
  return view_table

# Load worksheet.
examples = pd.read_csv(wiki_dialog_url, keep_default_na=False)

# View data.
view(examples[['pid', 'title', 'utterances']])

# + [markdown] id="mKwIfGoFN_qK"
# # 2. Filter data using the sensitive terms dataset
# Let's try and be a bit more targetted with our exploration using the Sensitive Terms dataset. The dataset consists of a taxonomy of sensitive terms and adjectives to aid fairness analysis.

# + id="qi-z7Ed6wyeS"
#@title Load the Sensitive Terms dataset.

sensitive_terms = pd.read_csv(sensitive_terms_url,
                              keep_default_na=False,
                              converters={
                                  'sub_cat': str,
                                  'sentiment': str,
                                  'sensitive_characteristic': str,
                              })

view(sensitive_terms)

# + id="0WvWNdCgOe2m"
#@title Implement matcher for sensitive terms.
# Create a regex matcher for the terms in the dataset. We can
#   use this matcher to efficiently find and extract terms
#   from the dataset that appear in sentences.
term_matcher = re.compile(r'\b(' + '|'.join(
    f'{term.lower()}' for term in sensitive_terms['term']) + r')\b')

def get_matched_terms(text):
  return set(term_matcher.findall(text.lower()))

example_sentence = "He is an abusive man."  #@param {type:"string"}
get_matched_terms(example_sentence)


# + id="qfxJu6hbPwM2"
#@title Filter the dataset to rows matching sensitive terms.  

def get_matched_terms_string(row):
  """A helper function to return the matched terms as a string."""
  matched_terms = get_matched_terms(row['utterances'])
  return ", ".join(matched_terms)

# Extend examples to include the matched terms as another column.
#   (axis=1) means that we will apply the above function to each row.
examples['matched_terms'] = examples.apply(get_matched_terms_string, axis=1)
examples_filtered_by_terms = examples[examples['matched_terms'] != '']
view(examples_filtered_by_terms[['pid', 'title', 'utterances', 'matched_terms']])

# + [markdown] id="jPHSuAfCUCkk"
# # 3. Refine the analysis using interactions
# One observation is that we need to refine our analysis by looking at interactions between the sensitive terms and adjectives. We include a table of interaction terms. 
#

# + id="X8oeODojR-OW"
#@title Load sensitive-interaction table.
interaction_table = pd.read_csv(interaction_table_url, keep_default_na=False)
interaction_table = interaction_table.set_index('Interaction Type')

view(interaction_table, include_index=True)

# + id="3E493OXgUCAH"
#@title Implement matcher for sensitive interactions.

# Each term can describe a sensitive characteristic or an adjective type.
# Store a mapping of them here.
sensitive_categories, adjective_categories = {}, {}
for _, row in sensitive_terms.iterrows():
  label = row['category']
  if row['sub_cat']:
    label += f": {row['sub_cat']}"
  if row['sentiment'] != 'NULL':
    label += f"/{row['sentiment']}"
  adjective_categories[row['term'].lower()] = label
  if row['sensitive_characteristic'] != "NULL":
    sensitive_categories[row['term'].lower()] = row['sensitive_characteristic']

# Convert the interaction table into an easier format to find.
sensitive_interactions = set()
for term1, row in interaction_table.items():
  for term2, value in row.items():
    if value == 'X':
      sensitive_interactions.add((term1.strip(), term2.strip()))

# Define a function to find interactions.
def get_matched_interactions(matched_terms):      
  """Find interactions between the `matched_terms` that might be sensitive."""
  interactions = []
  matched_terms = sorted(matched_terms)
  for i, term1 in enumerate(matched_terms):
    id1 = sensitive_categories.get(term1)
    adj1 = adjective_categories.get(term1)
    for term2 in matched_terms[i+1:]:
      id2 = sensitive_categories.get(term2)
      adj2 = adjective_categories.get(term2)
      if (id1, adj2) in sensitive_interactions:
        interactions.append(f'{id1} ({term1}) x {adj2} ({term2})')
      elif (id2, adj1) in sensitive_interactions:
        interactions.append(f'{id2} ({term2}) x {adj1} ({term1})')
  return set(interactions)

example = "aggressive men" #@param{type: 'string'}
matched_terms = get_matched_terms(example)
get_matched_interactions(matched_terms)


# + id="76pOpsHAxX35"
#@title Separate the given and generated text.
def get_generated_text(row):
  generated_questions = []
  for utterance in row['utterances'].split('\n'):
    if utterance.startswith("Q:"):
      generated_questions.append(utterance)
  return "\n".join(generated_questions)

def get_given_text(row):
  generated_questions = []
  for utterance in row['utterances'].split('\n'):
    if utterance.startswith("A:"):
      generated_questions.append(utterance)
  return "\n".join(generated_questions)

examples["generated_text"] = examples.apply(get_generated_text, axis=1)
examples["given_text"] = examples.apply(get_given_text, axis=1)

view(examples[['pid', 'title', 'generated_text', 'given_text']])


# + id="BrQ48aNPYfcl"
#@title Filter the dataset to rows that match sensitive interactions. 
# Filter rows that match any of the aforementioned terms.
def get_matched_interactions_string(row):
  generated_terms = get_matched_terms(row['generated_text'])
  given_terms = get_matched_terms(row['given_text'])
  generated_terms.difference_update(given_terms)
  matched_interactions = get_matched_interactions(generated_terms)
  return ", ".join(matched_interactions)

examples["matched_interactions"] = examples.apply(
    get_matched_interactions_string, axis=1)
examples_filtered_by_interactions = examples[
  examples["matched_interactions"] != ""]

# + id="YHsRY0y-ev3p"
#@title Count the number of interactions.
examples_filtered_by_interactions["pid"].describe()

# + id="4w1h2XeSe_g0"
#@title Let's look at the first 8 examples
view(examples_filtered_by_interactions.head(n=8)[
    ['pid', 'title', 'utterances', 'matched_terms', 'matched_interactions']])

StarcoderdataJupyterScriptsDedupFiltered