예제 #1
0
def quorum_exists_question(question: objects.Question, number_participants,
                           quorum_threshold):
    """
    Determines whether a sufficient percentage of Participants answered each question
    asked during a Meeting.

    Parameters
    ----------
    question
    number_participants
    quorum_threshold: The threshold above which quorum exists. Defaults to :data:`_QUORUM_THRESH_DEFAULT`

    Returns
    -------
    meta.Assertion
        A single assertion that where the value is True if a Quorum Exists on the Question or None if no Quorum exists.
    """
    if number_participants:
        engagement_on = engagement.engagement_raw(question.responses)
        engagement_ratio = engagement.engagement_relative(
            question.responses, number_participants)
        quorum_flag = (engagement_ratio > quorum_threshold) and (engagement_on
                                                                 > 3)
        return meta.Assertion(source=meta.System,
                              target=question,
                              value=quorum_flag)
    else:
        return meta.Assertion(source=meta.System, target=question, value=None)
예제 #2
0
def believable_consensus_exists(question: objects.Question) -> meta.Assertion:
    """
    TODO: Needs clarification on where it lives conceptually, what the I/O types should be, whether it can be refactored
    Consensus exists on a question.

    Parameters
    ----------
    question

    Returns
    -------
    bool
        Whether there is a consensus answer.
    """
    sufficient_engagement_flag = activity.quorum_exists_on_question_145(
        question)
    sufficient_believability_engagement_flag = activity.sufficient_believability_engagement(
        question)
    believable_choice_results = believable_choice_on_question(
        question) or isinstance(believable_choice_on_question(question), float)
    if sufficient_engagement_flag and sufficient_believability_engagement_flag and believable_choice_results:
        results = True
        return meta.Assertion(source=objects.System,
                              target=question,
                              value=results,
                              measure=objects.BooleanOption)
    else:
        results = False
        return meta.Assertion(source=objects.System,
                              target=question,
                              value=results,
                              measure=objects.BooleanOption)
예제 #3
0
def uniquely_out_of_sync_on_question_136(
        question: objects.Question) -> List[meta.Assertion]:
    """
    A person is uniquely out of sync on a question, if their response is unique (
    prios_api.disagreement.unique_choice) and out-of-sync with the believable consensus (
    prios_api.disagreement.out_of_sync_people_on_question).

    Parameters
    ----------
    question

    Returns
    -------
    List[meta.Assertion]
        Assertion for each person who answers the question. Value is whether they are uniquely
        out of sync.
    """
    unique_responses = disagreement.unique_choice(question)
    oos = disagreement.out_of_sync_people_on_question(question)
    results = list()
    for person_unique in unique_responses:
        for person_oos in oos:
            if person_unique.target == person_oos.target:
                uniquely_oos = person_oos.value and person_unique.value
        results.append(
            meta.Assertion(source=objects.System,
                           target=person_unique,
                           value=uniquely_oos))
    return results
예제 #4
0
def unique_choice(
        question: objects.Question,
        unique_disagreement=_UNIQUE_DISAGREEMENT) -> List[meta.Assertion]:
    """
    Determines whether responses are in small minority of responses.

    TODO: bucketing should live in it's own function
    Identify if the given 'response' is unique in relation to the given set of 'responses'.


    Parameters
    ----------
    question
    unique_disagreement
        Response is unique if its percentage (compared to all responses) is less than this value

    Returns
    -------
    List[meta.Assertion]
        System assertions of whether each response is unique
    """
    values = [xi.value for xi in question.responses]
    all_buckets = foundation.map_values(values, objects.NumericRange)
    percent = foundation.counts(all_buckets, normalize=True)
    results = list()
    for response in question.responses:
        if percent[response.value] < unique_disagreement:
            unique = True
        else:
            unique = False
        results.append(
            meta.Assertion(source=objects.System,
                           target=response.source,
                           value=unique))
    return results
예제 #5
0
def significantly_out_of_sync_in_meeting(
        meeting: objects.Meeting,
        threshold_low=0.8,
        threshold_high=1.2) -> List[meta.Assertion]:
    """
    A person is out of sync on significantly higher number of questions than others in a meeting.

    Parameters
    ----------
    meeting
    threshold_low
        A Person is Significantly OOS if they are notable and the Z-score of the number of
        questions on which they were OOS exceeds this quantity.
    threshold_high
        A Person is Significantly OOS if they are not notable and the Z-score of the number of
        questions on which they were OOS exceeds this quantity.

    Returns
    -------
    List[meta.Assertion]
        Value = True if person is Significantly OOS.
    """
    oos = {
        q.id: out_of_sync_people_on_question(q.id)
        for q in meeting.questions
    }

    oos_count = {person: 0 for person in meeting.participants}

    for q, is_oos in oos.items():
        for person in is_oos:
            if person.value:
                oos_count[person] += 1

    # TODO: Factor this better.
    oos_count_z_score = foundation.zscore(
        [value for key, value in oos_count.items()])
    oos_count_z_score_dict = dict()
    j = 0
    for key, value in oos_count.items():
        oos_count_z_score_dict[key] = oos_count_z_score[j]
        j += 1

    notable_people = activity.notable_participants(meeting)

    results = []
    for person, v in oos_count_z_score_dict.items():
        for person_notable in notable_people:
            if person.id == person_notable.target.id:
                if person_notable.value:
                    result_person = v > threshold_low
                else:
                    result_person = v > threshold_high

        results += [
            meta.Assertion(source=objects.System,
                           target=person,
                           value=result_person)
        ]
    return results
예제 #6
0
def believable_choice_on_question(
        question: objects.Question) -> meta.Assertion:
    """
    What is the believable choice on a question?

    Parameters
    ----------
    question

    Returns
    -------
    meta.Assertion
        Value represents the believable choice answer on the question.

    Examples
    --------
    >>> from prios_api.examples import binaryexample
    >>> believable_choice_on_question(binaryexample.question).value
    'No'
    >>> from prios_api.examples import categoricalexample
    >>> print(believable_choice_on_question(categoricalexample.question).value)
    None
    >>> from prios_api.examples import singleresponseexample
    >>> round(believable_choice_on_question(singleresponseexample.question).value, 2)
    1.0
    >>> from prios_api.examples import scaleexample
    >>> round(believable_choice_on_question(scaleexample.question).value, 2)
    7.0
    >>> from prios_api.examples import likertexample
    >>> round(believable_choice_on_question(likertexample.question).value, 2)
    3.16
    """

    # Extract responses and question data.
    values_and_weights = [(response.value, response.source.believability)
                          for response in question.responses]
    value_type = question.question_type

    # Get the Believable choice
    total_believability = sum([x[1] for x in values_and_weights])

    if not total_believability:
        result = None
    elif value_type in [
            objects.QuestionType.CATEGORICAL, objects.QuestionType.BINARY
    ]:
        result = believable_choice.believable_choice_categorical_binary(
            values_and_weights)
    elif value_type in [
            objects.QuestionType.LIKERT, objects.QuestionType.SCALE
    ]:
        result = foundation.weighted_average(
            [response.value for response in question.responses],
            [response.source.believability for response in question.responses])
    else:
        result = None

    return meta.Assertion(source=meta.System, target=question, value=result)
예제 #7
0
def disagrees_with_believable_choice(
        question: objects.Question) -> List[meta.Assertion]:
    """
    Whether the responses in a question disagree with the believable choice.

    Parameters
    ----------
    question

    Returns
    -------
    List[meta.Assertion]
        Values are True if person is out-of-sync on the question.

    Examples
    --------
    >>> from prios_api.examples import binaryexample
    >>> x = disagrees_with_believable_choice(binaryexample.question)
    >>> print([xi.value for xi in x])
    [False, True]
    >>> from prios_api.examples import categoricalexample
    >>> x = disagrees_with_believable_choice(categoricalexample.question)
    >>> print([xi.value for xi in x])
    []
    >>> from prios_api.examples import scaleexample
    >>> x = disagrees_with_believable_choice(scaleexample.question)
    >>> print([xi.value for xi in x])
    [False, False, False]
    >>> from prios_api.examples import likertexample
    >>> x = disagrees_with_believable_choice(likertexample.question)
    >>> print([xi.value for xi in x])
    [False, True, False, False, False]
    >>> from prios_api.examples import singleresponseexample
    >>> x = disagrees_with_believable_choice(singleresponseexample.question)
    >>> print([xi.value for xi in x])
    [False]
    """
    question_type = question.question_type
    believable_choice_result = believable_choice_on_question(question).value
    assertions = []
    if believable_choice_result:
        for response in question.responses:
            result = disagreement.disagrees_with_167(
                (response.value, believable_choice_result), question_type)
            assertions.append(
                meta.Assertion(source=meta.System,
                               target=response.source,
                               value=result))
    return assertions
예제 #8
0
def is_nubby_question(
        question: objects.Question,
        question_type,
        threshold: float = _THRESHOLD_STD_MAPPED_SCALE) -> meta.Assertion:
    """
    Is a question Nubby?

    Parameters
    ----------
    question
    question_type
    threshold
        Question is Nubby if its divisiveness value exceeds this quantity.

    Returns
    -------
    meta.Assertion
        Value is True if the question is Nubby.

    Examples
    --------
    >>> from prios_api.examples import binaryexample
    >>> print((is_nubby_question(binaryexample.question, question_type=objects.QuestionType.BINARY)).value)
    False
    >>> from prios_api.examples import likertexample
    >>> print((is_nubby_question(likertexample.question, question_type=objects.QuestionType.LIKERT)).value)
    True
    >>> from prios_api.examples import categoricalexample
    >>> print((is_nubby_question(categoricalexample.question, question_type=objects.QuestionType.CATEGORICAL)).value)
    True
    >>> from prios_api.examples import scaleexample
    >>> print((is_nubby_question(scaleexample.question, question_type=objects.QuestionType.SCALE)).value)
    False
    >>> from prios_api.examples import singleresponseexample
    >>> print((is_nubby_question(singleresponseexample.question, question_type=objects.QuestionType.LIKERT)).value)
    False
    """
    # TODO: Create extractor method in `objects.Question` that returns list of responses?
    values = [response.value for response in question.responses]
    if len(values) < 2:  # TODO: reasonable heuristic... where should it live?
        result = False
    else:
        result = divisiveness.divisiveness_stat(
            values, value_type=question_type) > threshold
    return meta.Assertion(target=question, value=result)
예제 #9
0
def nubby_question_popup_49(question: objects.Question) -> meta.Assertion:
    """
    Returns True if question is nubby and a quorum exists.

    Parameters
    ----------
    question

    Returns
    -------
    meta.Assertion
        Value is True if the Question is Nubby.
    """
    nubby = disagreement.is_nubby_question(question)
    quorum = activity.quorum_exists_question(question)
    return meta.Assertion(source=objects.System,
                          target=question,
                          value=nubby and quorum)
def believable_and_overall_meeting_section_sentiment_disagree_119(
        question: objects.Question) -> meta.Assertion:
    """
    Believable and Overall Meeting Section Sentiment Disagree

    TODO: This function assumes non-zero believabilities for every person

    Parameters
    ----------
    objects.Question
        The "Rate This Section" question object

    Returns
    -------
    meta.Assertion
        An assertion describing whether substantive disagreement exists between
        the believable and overall meeting section sentiment

    Examples
    --------
    >>> rate_the_section = objects.Question(title='Rate This Section', question_type=objects.QuestionType.RATING)
    >>> adam = objects.Person(name='Adam', believability=0.45)
    >>> bob = objects.Person(name='Bob', believability=0.05)
    >>> charlie = objects.Person(name='Charlie', believability=0.05)
    >>> rate_the_section.responses.append(objects.Response(source=adam, target=rate_the_section, value=10))
    >>> rate_the_section.responses.append(objects.Response(source=bob, target=rate_the_section, value=1))
    >>> rate_the_section.responses.append(objects.Response(source=charlie, target=rate_the_section, value=5))
    >>> result = believable_and_overall_meeting_section_sentiment_disagree_119(rate_the_section)
    >>> result.value
    1
    """
    assert question.question_type == objects.QuestionType.RATING, 'Question must be of type QuestionType.RATING!'

    responses = [(response.value, response.source.believability)
                 for response in question.responses]
    values, weights = list(zip(*responses))

    believable_sentiment = sentiment.sentiment(values, weights=weights)
    overall_sentiment = sentiment.sentiment(values)

    result = disagreement.substantive_disagreement(believable_sentiment,
                                                   overall_sentiment)
    return meta.Assertion(target=question, value=result)
예제 #11
0
def meeting_nubbiness_v1(
        meeting: objects.Meeting,
        thresholds: List[float] = [0.2, 0.4, 0.6, 0.8]) -> meta.Assertion:
    """
    Is a Meeting Nubby?

    TODO: Public.
    TODO: Default Thresholds are wrong.

    Parameters
    ----------
    meeting
    thresholds

    Returns
    -------
    meta.Assertion
        Value is a Classification.
    """
    nubby_questions = [q for q in meeting.questions if is_nubby_question(q)]

    def responses_to_list(question):
        return [q.response.value for q in nubby_questions.responses]

    if len(nubby_questions) == 0:
        meeting_divisiveness = 0.0
    else:
        meeting_divisiveness = [
            divisiveness(responses_to_list(q)) for q in nubby_questions
        ]

    scaling_factor = 0.5 if len(nubby_questions) == 1 else 0

    meeting_nubbiness = scaling_factor * meeting_divisiveness

    # TODO: Generalize classification object.
    # TODO: Add "classify" to Core Concepts.
    classification = objects.MeetingNubbyClassification(
        np.digitize(meeting_nubbiness, thresholds))
    return meta.Assertion(source=objects.System,
                          target=meeting.id,
                          value=classification,
                          measure=objects.MeetingNubbyClassification)
예제 #12
0
def sentiment(scale_assertions: objects.ScaleValueSet, **kwargs):
    """
    Defines "Sentiment" as the [Dots Summary](https://blakea-prios_api-registry.dev.principled.io/writeup?analytic=186) of all Dots in a Meeting.

    TODO: Is "Sentiment" akin to Believable Choice on Dots?
    TODO: Reconcile ScaleValueSet with List[meta.Assertion] and target for output Assertion.

    Args:
        scale_assertions (objects.CollectionOfScaleValues): A set of :type:float.
        *args: Variable length argument list.
        **kwargs: Arbitrary keyword arguments.

    Returns:
        objects.Assertion: A value representing sentiment, or None.
    """
    result = foundation.weighted_average(
        [i.value for i in scale_assertions.data], **kwargs)
    return meta.Assertion(source=objects.System,
                          target=scale_assertions,
                          value=result)
예제 #13
0
def dots_in_meeting_are_polarizing(
        meeting: objects.Meeting,
        by_action: Dict[str, str] = None) -> List[meta.Assertion]:
    """
    Returns Assertion on whether a List of Dots are Polarizing.

    Parameters
    ----------
    meeting
        Meeting object
    by_action
        Specifies mapping from Attribute to Action. Assumes that Attributes and Actions are
        represented as strings.

    Returns
    -------
    List[meta.Assertion]
        Whether or not Dots are polarizing (by action or for entire meeting)
    """
    # TODO: Represent Attributes by objects instead of strings.

    # TODO: Below is all Data Plumbing -- need to define utility function for it.
    dots_df = pd.DataFrame.from_records([(dot.source, dot.target, dot.value)
                                         for dot in meeting.dots],
                                        columns=['author', 'subject', 'value'])

    if by_action:
        dots_df['by'] = [by_action[dot.attribute.name] for dot in meeting.dots]
    else:
        dots_df['by'] = "all_dots"

    results = list()
    for action, df in dots_df.groupby(['by']):
        is_polar = disagreement.is_polarizing(list(df['value']))
        results.append(
            meta.Assertion(source=objects.System,
                           target=objects.Person,
                           value=is_polar,
                           label=action))
    return results
예제 #14
0
def dots_on_subject_are_polarizing(
        dots: List[objects.Dot]) -> List[meta.Assertion]:
    """
    Returns list of Assertions for each subject (target) in a list of Dots with a True/False
    value on whether the distribution of author-synthesized dot ratings that they received are
    polarizing.

    1. Values about a target given by each source is synthesized.
    2. Determines whether the synthesized values have a polarizing distribution.

    TODO: Disentangle into core concepts.

    Parameters
    ----------
    dots

    Returns
    -------
    List[meta.Assertion]
        Target of each Assertion is a topic/subject. Value is True if it is polarizing.
    """
    # TODO: Below is all Data Plumbing -- need to define utility function for it.
    author_subject_value = [(dot.source, dot.target, dot.value)
                            for dot in dots]
    author_subject_value_df = pd.DataFrame.from_records(
        author_subject_value, columns=['author', 'subject', 'value'])
    synthesized_ratings = author_subject_value_df.groupby(
        ['author', 'subject']).apply(concepts.syntheses.synthesize)
    synthesized_ratings = synthesized_ratings.to_frame().reset_index()
    polarizing_subjects = synthesized_ratings.groupby([
        'subject'
    ])['value'].apply(concepts.disagreement.is_polarizing).to_dict()

    results = list()
    for subject, polar in polarizing_subjects.items():
        results.append(
            meta.Assertion(source=objects.System, target=subject, value=polar))
    return results
예제 #15
0
def polarizing_participants_38(
        meeting: objects.Meeting) -> List[meta.Assertion]:
    """
    People who are polarizing in a Meeting, based on dots that they received and
    whether they were frequently dotted.

    Parameters
    ----------
    meeting

    Returns
    -------
    List[meta.Assertion]
        System assertion for each subject with value (Boolean) equal to True if they are
        viewed as being polarizing or False, otherwise.

    Examples
    --------
    >>> adam = objects.Person(person_id='Adam', uuid='Adam')
    >>> bob = objects.Person(person_id='Bob', uuid='Bob')
    >>> charlie = objects.Person(person_id='Charlie', uuid='Charlie')
    >>> dots = list()
    >>> result = polarizing_participants_38(objects.Meeting(dots=dots))
    >>> for x in result:
    ...     print(x.target.person_id, x.value)
    >>> dots = list()
    >>> dots.append(objects.Dot(source=adam, target=bob, value=1))
    >>> dots.append(objects.Dot(source=bob, target=charlie, value=10))
    >>> result = polarizing_participants_38(objects.Meeting(dots=dots))
    >>> for x in result:
    ...     print(x.target.person_id, x.value)
    Bob False
    Charlie False
    >>> dots = list()
    >>> dots.append(objects.Dot(source=bob, target=adam, value=1))
    >>> dots.append(objects.Dot(source=charlie, target=adam, value=10))
    >>> result = polarizing_participants_38(objects.Meeting(dots=dots))
    >>> for x in result:
    ...     print(x.target.person_id, x.value)
    Adam True
    >>> dots = list()
    >>> dots.append(objects.Dot(source=bob, target=adam, value=1))
    >>> dots.append(objects.Dot(source=charlie, target=adam, value=10))
    >>> dots.append(objects.Dot(source=bob, target=adam, value=10))
    >>> dots.append(objects.Dot(source=charlie, target=adam, value=1))
    >>> result = polarizing_participants_38(objects.Meeting(dots=dots))
    >>> for x in result:
    ...     print(x.target.person_id, x.value)
    Adam False
    """
    frequently_dotted = activity.frequently_dotted_subjects(meeting.dots)
    dots_are_polarizing = disagreement.dots_on_subjects_are_nubby_and_polarizing(
        meeting.dots)

    result = []
    for person_frequently_dotted in frequently_dotted:
        for person_polarizing in dots_are_polarizing:
            if person_frequently_dotted.target.person_id == person_polarizing.target.person_id:
                person = person_frequently_dotted.target
                result.append(
                    meta.Assertion(source=meta.System,
                                   target=person,
                                   value=(person_frequently_dotted.value
                                          and person_polarizing.value)))
    return result
예제 #16
0
def frequently_dotted_subjects(dots: List[objects.Dot],
                               min_percent_1: float = 0.10,
                               min_count_1: int = 0,
                               min_percent_2: float = 0.05,
                               min_count_2: int = 5) -> List[meta.Assertion]:
    """
    Determines whether a Subject is Frequently Dotted. receives more than either:
    * 10% of all Dot Ratings OR
    * 5% of all Dot Ratings along with 10 Dot Ratings.


    Parameters
    ----------
    dots
    min_percent_1
        A subject is frequently dotted if the percent of dots they receive exceeds this quantity
        and the number of dots exceeds `min_count_1` (default = 10%)
    min_count_1
        A subject is frequently dotted if the number of dots they receive exceeds this quantity
        and the percentage of dots exceeds `min_percent_1` (default = 0)
    min_percent_2
        A subject is frequently dotted if the percent of dots they receive exceeds this quantity
        and the number of dots exceeds `min_count_2` (default = 5%)
    min_count_2
        A subject is frequently dotted if the number of dots they receive exceeds this quantity
        and the percentage of dots exceeds `min_percent_2` (default = 5)

    Returns
    -------
    List[meta.Assertion]
        System assertion for each subject with value (Boolean) equal to True if they are
        frequently dotted or False, otherwise.

    Examples
    --------
    >>> adam = objects.Person(name='Adam', uuid='Adam')
    >>> bob = objects.Person(name='Bob', uuid='Bob')
    >>> charlie = objects.Person(name='Charlie', uuid='Charlie')
    >>> bob_receives_every_dot = [objects.Dot(source=bob, target=adam, value=10)] * 2
    >>> for x in frequently_dotted_subjects(bob_receives_every_dot):
    ...     print(x.target.name, x.value)
    Bob True
    >>> dots_split_between_two_people = list()
    >>> dots_split_between_two_people += [objects.Dot(source=charlie, target=adam, value=1)] * 2
    >>> dots_split_between_two_people += [objects.Dot(source=charlie, target=bob, value=1)] * 2
    >>> for x in frequently_dotted_subjects(dots_split_between_two_people):
    ...     print(x.target.name, x.value)
    Adam True
    Bob True
    >>> one_person_gets_most_dots = list()
    >>> one_person_gets_most_dots += [objects.Dot(source=charlie, target=adam, value=1)] * 2
    >>> one_person_gets_most_dots += [objects.Dot(source=charlie, target=bob, value=1)] * 2
    >>> one_person_gets_most_dots += [objects.Dot(source=charlie, target=charlie, value=1)] * 100
    >>> for x in frequently_dotted_subjects(one_person_gets_most_dots):
    ...     print(x.target.name, x.value)
    Adam False
    Bob False
    Charlie True
    """

    # Identify unique subjects.
    subjects = {dot.target.uuid: dot.target for dot in dots}

    result = []
    for subject_uuid, subject in subjects.items():
        subject_dots = [dot for dot in dots if dot.target.uuid == subject_uuid]
        percent_dots = engagement.engagement_relative(subject_dots, len(dots))
        total_dots = engagement.engagement_raw(subject_dots)
        cond1 = percent_dots > min_percent_1 and total_dots > min_count_1
        cond2 = percent_dots > min_percent_2 and total_dots > min_count_2
        subject_is_frequently_dotted = meta.Assertion(source=meta.System,
                                                      target=subject,
                                                      value=cond1 or cond2)
        result += [subject_is_frequently_dotted]

    return result
예제 #17
0
def dots_on_subjects_are_nubby_and_polarizing(dots: List[objects.Dot],
                                              thresholds: Dict[str, float] = _THRESHOLD_DICT) \
                                              ->List[meta.Assertion]:
    """
    Returns list of Assertions for each subject (target) in a list of Dots with a True/False
    value on whether the distribution of author-synthesized dot ratings that they received are
    "polarizing".

    1. Values about a target given by each source is synthesized.
    2. Determines whether the synthesized values have a polarizing distribution. A distribution of
    values is polarizing if the following statistics exceed threshold values
    - its standard deviation (divisiveness)
    - the standard deviation of the values mapped to negative/neutral/positive (semantic
    divisiveness)
    - the ratio and inverse ratio between the numbers of positive opinions (>= 7) and negative
    opinions (<5) (polarization)

    Parameters
    ----------
    dots
    thresholds
        Dictionary containing thresholds at keys for:
        * 'divisiveness': Divisiveness of raw values (Default = 1.0)
        * 'mapped_divisiveness': Divisiveness of values mapped to semantic scale (Default = 0.5)
        * 'polarization': Minimum of ratio between positive to negative opinions and ratio
        between negative to positive opinions (Default = 0.25)

    Returns
    -------
    List[meta.Assertion]
        Target of each Assertion is a subject. Value is True if distribution of dot ratings (
        synthesized by author) is nubby and polarizing .

    Examples
    --------
    >>> adam = objects.Person(name='Adam', uuid='Adam')
    >>> bob = objects.Person(name='Bob', uuid='Bob')
    >>> charlie = objects.Person(name='Charlie', uuid='Charlie')
    >>> dots = list()
    >>> dots.append(objects.Dot(source=adam, target=bob, value=10))
    >>> dots.append(objects.Dot(source=adam, target=bob, value=10))
    >>> result = dots_on_subjects_are_nubby_and_polarizing(dots)
    >>> for x in result:
    ...     print(x.target.name, x.value)
    Bob False
    >>> dots.append(objects.Dot(source=charlie, target=bob, value=1))
    >>> dots.append(objects.Dot(source=charlie, target=bob, value=1))
    >>> dots.append(objects.Dot(source=bob, target=adam, value=10))
    >>> result = dots_on_subjects_are_nubby_and_polarizing(dots)
    >>> for x in result:
    ...     print(x.target.name, x.value)
    Bob True
    Adam False
    >>> dots.append(objects.Dot(source=charlie, target=adam, value=1))
    >>> dots.append(objects.Dot(source=charlie, target=adam, value=1))
    >>> dots.append(objects.Dot(source=bob, target=adam, value=10))
    >>> dots.append(objects.Dot(source=bob, target=adam, value=10))
    >>> result = dots_on_subjects_are_nubby_and_polarizing(dots)
    >>> for x in result:
    ...     print(x.target.name, x.value)
    Bob True
    Adam True
    """
    def key_func(x):
        return x.source.uuid, x.target.uuid

    # Synthesize author opinions of each subject.
    values_grp_by_author_subject = utils.group_assertions_by_key(
        dots, key_func=key_func)
    subjects = dict()
    for x in values_grp_by_author_subject:
        one_synthesis = synthesis.synthesize([dot.value for dot in x[1]])
        if x[0][1] in subjects.keys():
            subjects[x[0][1]].append(one_synthesis)
        else:
            subjects[x[0][1]] = [one_synthesis]

    uuid_to_person = {dot.target.uuid: dot.target for dot in dots}

    # Identify subjects whose author syntheses satisfy divisiveness and polarization conditions.
    results = list()
    for sub, values in subjects.items():
        values_divisiveness = divisiveness.divisiveness_stat(
            values, objects.QuestionType.SCALE, map_to_sentiment=False)
        mapped_values_divisiveness = divisiveness.divisiveness_stat(
            values, objects.QuestionType.SCALE)
        values_polarization = polarizing.polarizing_stat(values)
        is_polarizing = (values_divisiveness > thresholds['divisiveness'] and \
                         mapped_values_divisiveness > thresholds['mapped_divisiveness'] and \
                         values_polarization  > thresholds['polarization'])
        results.append(
            meta.Assertion(source=meta.System,
                           target=uuid_to_person[sub],
                           value=is_polarizing))

    return results