<jupyter_start><jupyter_text>Goal:This notebook was created to inspect the difference between the `Hardcore` workflow data and that of the other workflows. None of the code here is used in the final analysis, this served only to explore the shape of the data, but the notebook remains here for posterity.<jupyter_code>import json
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from datetime import date
%matplotlib inline

import sys
sys.path.append('../..')
from sf_lib.sf import (
    getFilename, 
    getMetadataValue, 
    parseTime, 
    getGroupSize, 
    extractTaskValue
)

from sf_lib.helpers import json_parser
# reload sf import while I'm working on extracting functionality to it from notebooks
from importlib import reload
import sf_lib
reload(sf_lib), reload(sf_lib.sf), reload(sf_lib.df)<jupyter_output><empty_output><jupyter_text>`Hardcore` SpaceFluff category  General information on this workflow:- T0: 'Look at the very center of the image: do you see a single galaxy or a group of far away objects?'- T2: 'Is the galaxy fluffy or is it bright?'- T1: 'What color is the galaxy?'- T3: 'Does the galaxy have a visible core?'- T4: 'What shape is the galaxy?'- T5: 'How would you describe the texture of the galaxy?'- T9: 'Our bad! what do you see instead?'- T0 is always asked.     - If user answers 'galaxy', then T2 through T5 are asked (in order listed above)    - If user answers 'something else/empty center', they're asked T9.  Load DataFrame, parse columns: - load the csv into a dataframe,- parse stringified columns,- add Filename field- filter by date (exclude classifications made in beta stage)- drop unused columns `gold_standard` and `expert`- maybe filter by latest workflow version<jupyter_code>object_info = pd.read_csv('../../catalogue/sf_spacefluff_object_data.csv', comment="#")
candidate_names_classify = np.loadtxt('../sf_candidate_names__classification-classify.txt', dtype='str')
catalogue_targets = np.loadtxt('../../catalogue/sf_catalogue_targets.txt', dtype='str')
converters = {
    'subject_data': json_parser,
    'annotations': json_parser,
    'metadata': json_parser 
}
# load classifications
df = pd.read_csv('../../SpaceFluff/zooniverse_exports/classify-hardcore-edition-classifications.csv', converters=converters, delimiter=",")

# add Filename column at index 0
df.insert(0, 'Filename', df['subject_data'].apply(getFilename))
    
# filter by classification date (remove entries from before ~ October 20, 2020 as that's presumably the end of beta)
df['created_at'] = parseTime(df['created_at'])
end_of_beta = pd.Timestamp(date(2020,10,20), tz='utc')
df = df[df['created_at'] > end_of_beta]
    
# as far as I can tell, 'gold_standard' and 'expert' are never not NaN, so just remove them for readibility
df.drop(['gold_standard', 'expert'], axis=1, inplace=True)

# keep only classifications made on latest workflow version. @note: this might not be necessary
workflow_versions = df['workflow_version'].unique()
# df = df[df['workflow_version'] == workflow_versions[-1]]
df = df.query("workflow_version == @workflow_versions[-1]")

# create temporary df to extract isRetired and alreadySeen
df_temp = pd.DataFrame(columns=df.columns)

# drop rows where already_seen or retired. This way, we don't count two classifications for the same object from the same user,
#   extract retired and already_seen columns in this temporary dataframe
for field in ['retired', 'already_seen']:
    df_temp[field] = df['metadata'].apply(lambda x: getMetadataValue(x, field))
    
df = df[~df_temp['retired'] & ~df_temp['already_seen']]    
del df_temp

# extract the answer of each task to a separate column
task_indices = [0, 2, 1, 3, 4, 5, 9]
tasks = ['T{}'.format(i) for i in task_indices]

for task in tasks:
    df[task] = df['annotations'].apply(lambda x: extractTaskValue(x, task))
df.keys()<jupyter_output><empty_output><jupyter_text>Inspect distribution of vote count:<jupyter_code>group_by_name = df[['Filename']].groupby(['Filename'])
# check number of unique objects:
print('number of unique filenames:', df['Filename'].unique().shape[0])  # or group_by_name.groups.keys()
object_names = list(group_by_name.groups.keys())
votes_per_object = []
for name in object_names:
    count = getGroupSize(group_by_name.get_group(name))
    votes_per_object.append({
        'name': name,
        'votes': count
    })
    
vote_counts = [entry['votes'] for entry in votes_per_object]
plt.hist(vote_counts)
plt.show()<jupyter_output><empty_output><jupyter_text>Print unique responses to every task:<jupyter_code>header = '{:<4s} | {}'.format('task', 'unique answers')
print(header)
for task in tasks:
    print('{:>4s} | {}'.format(task, df[[task]][task].unique()))<jupyter_output>task | unique answers
  T0 | ['Galaxy' 'Group of objects (Cluster)' 'Something else/empty center']
  T2 | ['Fluffy' None 'Bright']
  T1 | ['Impossible to say' 'White/blue' None 'Red/yellow']
  T3 | ['No/Unsure' None 'Yes, a bright point' 'Yes, a bulge']
  T4 | ['Distorted/disturbed' 'Elliptical' None 'Round']
  T5 | [None 'Smooth and fuzzy' 'Smooth and dense' 'Clumpy and/or featured']
  T9 | [None 'Something else' 'Looks like a small star' 'Scattered light'
 'Nothing: background too bright or galaxy too faint']
{'fraction_non_alphanumeric': 0.12036508841985168, 'fraction_numerical': 0.00893706027761932, 'mean_word_length': 5.130536130536131, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 18, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '609606', 'license': 'MIT', 'n_tokens_mistral': 1687, 'n_tokens_neox': 1627, 'n_words': 556, 'path': 'analysis/hardcore workflow/sf_hardcore.ipynb', 'repo_name': 'Seerden/SpaceFluff', 'size': '5259'}