johnpaulbin's picture
Update app.py
f823d62 verified
raw
history blame
9.42 kB
import gradio as gr
import matplotlib.pyplot as plt
from io import BytesIO
from PIL import Image as PILImage
# Define the 44 BFI questions
questions = [
"1. Talks a lot",
"2. Notices other people’s weak points",
"3. Does things carefully and completely",
"4. Is sad, depressed",
"5. Is original, comes up with new ideas",
"6. Keeps their thoughts to themselves",
"7. Is helpful and not selfish with others",
"8. Can be kind of careless",
"9. Is relaxed, handles stress well",
"10. Is curious about lots of different things",
"11. Has a lot of energy",
"12. Starts arguments with others",
"13. Is a good, hard worker",
"14. Can be tense; not always easy going",
"15. Clever; thinks a lot",
"16. Makes things exciting",
"17. Forgives others easily",
"18. Isn’t very organized",
"19. Worries a lot",
"20. Has a good, active imagination",
"21. Tends to be quiet",
"22. Usually trusts people",
"23. Tends to be lazy",
"24. Doesn’t get upset easily; steady",
"25. Is creative and inventive",
"26. Has a good, strong personality",
"27. Can be cold and distant with others",
"28. Keeps working until things are done",
"29. Can be moody",
"30. Likes artistic and creative experiences",
"31. Is kind of shy",
"32. Kind and considerate to almost everyone",
"33. Does things quickly and carefully",
"34. Stays calm in difficult situations",
"35. Likes work that is the same every time",
"36. Is outgoing; likes to be with people",
"37. Is sometimes rude to others",
"38. Makes plans and sticks to them",
"39. Get nervous easily",
"40. Likes to think and play with ideas",
"41. Doesn’t like artistic things (plays, music)",
"42. Likes to cooperate; goes along with others",
"43. Has trouble paying attention",
"44. Knows a lot about art, music and books"
]
# Scoring function based on the provided SPSS syntax
def compute_bfi_scores(*args):
responses = list(args)
# Convert 'No response' to None, else to int
processed = []
for r in responses:
if r == "No response":
processed.append(None)
else:
processed.append(int(r))
# Define traits with their respective items and scoring parameters
traits = {
'Extraversion': {
'positive': [1, 11, 16, 26, 36],
'reverse': [6, 21, 31],
'threshold': 1,
'formula_pos_mult': 5,
'formula_reverse_mult': 3,
'formula_reverse_const': 12
},
'Agreeableness': {
'positive': [7, 17, 22, 32, 42],
'reverse': [2, 12, 27, 37],
'threshold': 1,
'formula_pos_mult': 5,
'formula_reverse_mult':4,
'formula_reverse_const':16
},
'Conscientiousness': {
'positive': [3, 13, 28, 33, 38],
'reverse': [8, 18, 23, 43],
'threshold': 1,
'formula_pos_mult':5,
'formula_reverse_mult':4,
'formula_reverse_const':16
},
'Neuroticism':{
'positive':[4, 14, 19, 29, 39],
'reverse':[9, 24, 34],
'threshold':1,
'formula_pos_mult':5,
'formula_reverse_mult':3,
'formula_reverse_const':12
},
'Openness':{
'positive':[5, 10, 15, 20, 25, 30, 40, 44],
'reverse':[35, 41],
'threshold':2,
'formula_pos_mult':8,
'formula_reverse_mult':2,
'formula_reverse_const':8
}
}
scores = {}
for trait, info in traits.items():
pos_items = [processed[i-1] for i in info['positive']]
rev_items = [processed[i-1] for i in info['reverse']]
missing_pos = pos_items.count(None)
missing_rev = rev_items.count(None)
total_missing = missing_pos + missing_rev
if total_missing > info['threshold']:
scores[trait] = "Incomplete"
else:
# Compute means, ignoring None
pos_values = [x for x in pos_items if x is not None]
rev_values = [x for x in rev_items if x is not None]
mean_pos = sum(pos_values) / len(pos_values) if pos_values else 0
mean_rev = sum(rev_values) / len(rev_values) if rev_values else 0
# Apply the scoring formula
score = (mean_pos * info['formula_pos_mult']) + (info['formula_reverse_const'] - (mean_rev * info['formula_reverse_mult']))
score = round(score, 2)
scores[trait] = score
# Prepare the output in Markdown format with explanations
explanations = {
'Extraversion': "Extraversion reflects how outgoing and energetic you are. High scores indicate sociability and enthusiasm, while low scores suggest a more reserved and solitary nature.",
'Agreeableness': "Agreeableness measures your tendency to be compassionate and cooperative. High scores signify kindness and trust, whereas low scores may indicate competitiveness or skepticism.",
'Conscientiousness': "Conscientiousness assesses your level of self-discipline and organization. High scores denote reliability and thoroughness, while low scores might reflect a more spontaneous or disorganized approach.",
'Neuroticism': "Neuroticism indicates emotional stability and susceptibility to stress. High scores suggest a tendency towards anxiety and moodiness, whereas low scores imply calmness and resilience.",
'Openness': "Openness measures your openness to new experiences and creativity. High scores are associated with imagination and curiosity, while low scores may indicate practicality and preference for routine."
}
markdown_output = "## Your Big Five Personality Traits Scores\n\n"
# Prepare data for visualization
trait_names = []
trait_scores = []
for trait, score in scores.items():
markdown_output += f"### **{trait}**\n"
if score == "Incomplete":
markdown_output += "Insufficient responses to compute this trait.\n\n"
else:
markdown_output += f"**Score**: {score}\n\n"
markdown_output += f"{explanations[trait]}\n\n"
trait_names.append(trait)
trait_scores.append(score)
# Generate bar chart
image = None
if trait_scores:
fig, ax = plt.subplots(figsize=(8, 4))
bars = ax.bar(trait_names, trait_scores, color='skyblue')
ax.set_ylim(0, max(trait_scores) + 10)
ax.set_ylabel('Score')
ax.set_title('Big Five Traits Scores')
# Add score labels on top of bars
for bar in bars:
height = bar.get_height()
ax.annotate(f'{height}',
xy=(bar.get_x() + bar.get_width() / 2, height),
xytext=(0, 3), # 3 points vertical offset
textcoords="offset points",
ha='center', va='bottom')
plt.tight_layout()
# Save the plot to a PNG image in memory
buf = BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
plt.close(fig)
# Convert bytes to PIL Image
image = PILImage.open(buf)
buf.close()
markdown_output += "### **Trait Scores Visualization**\n\n"
return markdown_output, image
# Create the Gradio interface
def create_interface():
with gr.Blocks() as demo:
gr.Markdown("# Big Five Inventory (BFI) Quiz")
gr.Markdown(
"""
Please rate the following statements on a scale from **1 (Disagree a lot)** to **5 (Agree a lot)**.
If you prefer not to respond to a particular statement, select **'No response'**.
"""
)
# Organize questions into expandable sections by trait
trait_question_map = {
'Extraversion': [1, 6, 11, 16, 21, 26, 31, 36],
'Agreeableness': [2, 7, 12, 17, 22, 27, 32, 37, 42],
'Conscientiousness': [3, 8, 13, 18, 23, 28, 33, 38, 43],
'Neuroticism': [4, 9, 14, 19, 24, 29, 34, 39],
'Openness': [5, 10, 15, 20, 25, 30, 35, 40, 41, 44]
}
inputs = []
with gr.Accordion("Answer the Questions", open=True):
for trait, q_indices in trait_question_map.items():
with gr.Accordion(trait, open=False):
for i in q_indices:
q = questions[i-1]
radio = gr.Radio(
choices=["No response", 1, 2, 3, 4, 5],
label=q,
value="No response",
interactive=True
)
inputs.append(radio)
# Submit button
submit_btn = gr.Button("Submit", variant="primary")
# Results display
with gr.Row():
markdown_result = gr.Markdown(label="Textual Results")
image_result = gr.Image(label="Trait Scores Visualization")
# Link the button to the function
submit_btn.click(
fn=compute_bfi_scores,
inputs=inputs,
outputs=[markdown_result, image_result]
)
return demo
# Launch the interface
demo = create_interface()
demo.launch()