Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Statistical analysis for medical research papers. Generates reproducible Python/R code with publication-ready tables and figures. Supports diagnostic accuracy, inter-rater agreement, meta-analysis, survival analysis, survey data, group comparisons, regression, propensity score, and repeated measures.
SkillScore55/100
Test results 55
3/3 safety checks passed
45%55%of test cases passed +9 pts
Passed 12 of 22 test cases with the skill, and 10 of 22 without it.
4 fixed8 passed either way8 still failing2 broke
This benchmark measured an earlier version of the skill.
A newer attempt on gemini-3.6-flash on 10/12/2026 did not produce a publishable comparison (graded differently). The snapshot above is the previously measured run.
One test taskWe plan to run a Cochran-Armitage trend test on 3 disease stages (Stage I, Stage II, Stage III) predicting 1-year mortality in `2_Data/staging_cohort.csv`. What verification step must be performed on the strata before running the test?
Adds every file the skill has, pinned so you can update it. No account needed.
pip install "decimalai>=0.15"
decimalai skills add aperivue-analyze-stats
claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"The same install from Python, for a notebook or a setup script.
pip install "decimalai>=0.15"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
print("Added. Start Claude Code with the command below.")claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"Type these in a Claude Code session. No account or API key. It writes the skill and any files it bundles, and asks first when its safety rating isn't Passed.
/plugin marketplace add decimal-labs/decimalai-claude-plugins
/plugin install decimalai@decimalai/decimalai:install aperivue-analyze-statsThe plugin needs python3 and curl on your machine.
No Python or account. Claude Code picks it up in its next session.
curl -fsSL --create-dirs -o .claude/skills/aperivue-analyze-stats/SKILL.md https://app.decimal.ai/s/aperivue-analyze-stats/SKILL.md.claude/skills/aperivue-analyze-stats/SKILL.mdThis gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"
decimalai skills add aperivue-analyze-statsimport asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/aperivue-analyze-stats/SKILL.mdimport asyncio
from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions, query
skill = Path(".agents/skills/aperivue-analyze-stats/SKILL.md").read_text()
options = ClaudeAgentOptions(system_prompt=f"You are a helpful agent.\n\n{skill}", max_turns=6)
async def main():
async for message in query(prompt="Hello!", options=options):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())This gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"
decimalai skills add aperivue-analyze-statsfrom agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
from agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/aperivue-analyze-stats/SKILL.mdfrom pathlib import Path
from agents import Agent, Runner
skill = Path(".agents/skills/aperivue-analyze-stats/SKILL.md").read_text()
agent = Agent(name="support", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")This gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"
decimalai skills add aperivue-analyze-statsimport json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
import json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/aperivue-analyze-stats/SKILL.mdfrom pathlib import Path
from openai import OpenAI
client = OpenAI()
skill = Path(".agents/skills/aperivue-analyze-stats/SKILL.md").read_text()
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[
{"role": "system", "content": f"You are a helpful agent.\n\n{skill}"},
{"role": "user", "content": "Hello!"},
],
).choices[0].messageThis gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"
decimalai skills add aperivue-analyze-statsimport anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
import anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/aperivue-analyze-stats/SKILL.mdfrom pathlib import Path
import anthropic
client = anthropic.Anthropic()
skill = Path(".agents/skills/aperivue-analyze-stats/SKILL.md").read_text()
reply = client.messages.create(
model="claude-haiku-5-5",
max_tokens=4096,
system=f"You are a helpful agent.\n\n{skill}",
messages=[{"role": "user", "content": "Hello!"}],
)This gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"
decimalai skills add aperivue-analyze-statsfrom google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
from google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/aperivue-analyze-stats/SKILL.mdfrom pathlib import Path
from google import genai
from google.genai import types
client = genai.Client()
skill = Path(".agents/skills/aperivue-analyze-stats/SKILL.md").read_text()
response = client.models.generate_content(
model="gemini-3.5-flash",
contents="Hello!",
config=types.GenerateContentConfig(system_instruction=f"You are a helpful agent.\n\n{skill}"),
)This gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openai
decimalai skills add aperivue-analyze-statsfrom langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openaifrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
from langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/aperivue-analyze-stats/SKILL.mdfrom pathlib import Path
from langchain.agents import create_agent
skill = Path(".agents/skills/aperivue-analyze-stats/SKILL.md").read_text()
agent = create_agent("openai:gpt-5.4-mini", tools=[], system_prompt=f"You are a helpful agent.\n\n{skill}")
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 6 other files are not included.
CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"
decimalai skills add aperivue-analyze-statsimport decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()The same install from Python, for a notebook or a setup script. CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()No DecimalAI package. CrewAI's own loader reads the folder, and nothing pins or updates it.
.agents/skills/aperivue-analyze-stats/SKILL.mdfrom pathlib import Path
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=[Path(".agents/skills")],
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()This gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"
decimalai skills add aperivue-analyze-statsfrom decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/aperivue-analyze-stats/SKILL.mdfrom pathlib import Path
from google.adk.agents import LlmAgent
skill = Path(".agents/skills/aperivue-analyze-stats/SKILL.md").read_text()
agent = LlmAgent(
name="support",
model="gemini-3.5-flash",
instruction=lambda context: f"You are a helpful agent.\n\n{skill}",
)This gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"
decimalai skills add aperivue-analyze-statsfrom pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
from pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/aperivue-analyze-stats/SKILL.mdfrom pathlib import Path
from pydantic_ai import Agent
skill = Path(".agents/skills/aperivue-analyze-stats/SKILL.md").read_text()
agent = Agent("openai:gpt-4o-mini", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")This gets SKILL.md only. The skill's 6 other files are not included.
Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagents
decimalai skills add aperivue-analyze-statsimport decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
The same install from Python, for a notebook or a setup script. Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/aperivue-analyze-stats/SKILL.mdfrom pathlib import Path
from deepagents import create_deep_agent
skill = Path(".agents/skills/aperivue-analyze-stats/SKILL.md").read_text()
agent = create_deep_agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 6 other files are not included.
Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-framework
decimalai skills add aperivue-analyze-statsimport asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
The same install from Python, for a notebook or a setup script. Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-frameworkfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/aperivue-analyze-stats/SKILL.mdimport asyncio
from pathlib import Path
from agent_framework import Agent
skill = Path(".agents/skills/aperivue-analyze-stats/SKILL.md").read_text()
agent = Agent(client=client, instructions=f"You are a helpful agent.\n\n{skill}")
asyncio.run(agent.run("Hello!"))This gets SKILL.md only. The skill's 6 other files are not included.
Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agents
decimalai skills add aperivue-analyze-statsfrom decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
The same install from Python, for a notebook or a setup script. Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("aperivue-analyze-stats")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/aperivue-analyze-stats/SKILL.mdfrom pathlib import Path
from strands import Agent
skill = Path(".agents/skills/aperivue-analyze-stats/SKILL.md").read_text()
agent = Agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent("Hello!")This gets SKILL.md only. The skill's 6 other files are not included.
Adds every file the skill has, pinned so you can update it. Cursor, Copilot, Cline and Warp read this folder by themselves.
pip install "decimalai>=0.15"
decimalai skills add aperivue-analyze-statsNo Python or account. Cursor, Copilot, Cline and Warp read this folder by themselves.
curl -fsSL --create-dirs -o .agents/skills/aperivue-analyze-stats/SKILL.md https://app.decimal.ai/s/aperivue-analyze-stats/SKILL.md.agents/skills/aperivue-analyze-stats/SKILL.mdThis gets SKILL.md only. The skill's 6 other files are not included.
Give its text to your model as instructions. No account or package needed.
This gets SKILL.md only. The skill's 6 other files are not included.
Raw files: /s/aperivue-analyze-stats/SKILL.md · /s/aperivue-analyze-stats.json
Fetch the file and give its text to your model as instructions. No account or package needed.
curl -fsSL https://app.decimal.ai/s/aperivue-analyze-stats/SKILL.mdcurl -fsSL https://app.decimal.ai/s/aperivue-analyze-stats.jsonThis gets SKILL.md only. The skill's 6 other files are not included.
You don't need a DecimalAI account.
Saved prompts and skills, pinned versions, and traces of this skill in your runs. DecimalAI does not run your agent.
Claude Code reads files from your project, so DecimalAI config does not apply there.
On Claude Agent SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI Agents: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Anthropic: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Gemini API: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On LangChain: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On CrewAI: set DECIMAL_API_KEY and add decimalai.init(crewai=True) to record runs and skill use. The skill version stays in your repo.
On Google ADK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Pydantic AI: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Deep Agents: set DECIMAL_API_KEY and add decimalai.init(langchain=True) to record runs and skill use. The skill version stays in your repo.
On Microsoft Agent Framework: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
On Strands: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
Editors read files from your project, so DecimalAI config does not apply there.
Anywhere else: record a use with decimalai.log_skill_activation() inside a trace.
Generate reproducible code (Python preferred, R when necessary) for medical research analyses, run it, and produce publication-ready tables and figures from its output.
Before reading any data file, check whether it might contain Protected Health Information (PHI):
*_deidentified.* files exist in the working directory, use those./deidentify./deidentify.${CLAUDE_SKILL_DIR}/references/templates/ — reusable analysis scripts; read the matching one
before generating code.${CLAUDE_SKILL_DIR}/references/analysis_guides/ — methodology guides; the Phase 2 table says
which to read.${CLAUDE_SKILL_DIR}/references/table-standards/ — table-standards.md (universal and AMA
rules, footnote order, gtsummary pipeline), journal-profiles/ (YAML per journal: radiology,
jama, nejm, lancet, european_radiology, ajr), table-types/ (one template per table type),
tool-comparison.md (R/Python table tools).${CLAUDE_SKILL_DIR}/references/style/figure_style.mplstyle — figure style.[VERIFY: variable_name] and ask the user to
confirm it against the data dictionary; never guess a column name or coding.Observational designs (cohort, case-control, cross-sectional, registry, survey): before
planning, look for a variable_operationalization.md from /define-variables (or an equivalent
codebook-backed definition table). If none exists, warn the user and recommend running
/define-variables first, because exposure/outcome/covariate definitions and cutoffs invented
ad hoc from the data dictionary are a common reason reviewers reject observational work. This is
a warning, not a block: proceed on explicit user confirmation and record that the artifact was
not available.
Propose an analysis plan; the user decides the research question and endpoints. Mark any
clinical definition, cutoff, diagnostic criterion or guideline claim you could not confirm
[VERIFY].
Detect the analysis type from the table in Analysis-Specific Guidelines, or accept the user's specification.
List the specific tests to be performed.
Identify primary and secondary endpoints.
State the assumptions that will be checked (normality, homogeneity, independence).
Note data cleaning needed (recoding, outlier handling, missing-data strategy).
Anchor the estimand to the research question.
interactionR / epiR; Knol & VanderWeele reporting), not only a
multiplicative product term, because a non-significant multiplicative interaction is
compatible with a large additive one (and vice versa). Joint categories (high/high vs
low/low) show joint association, not interaction; "stronger in A than in B" from separate
stratum estimates is the difference-in-significance fallacy — report the formal interaction
term.Screen every categorical/binary predictor for separation before fitting anything. A
predictor that perfectly predicts the outcome has no finite MLE, and the failure is silent:
glm returns an odds ratio near 0 (or enormous), p ≈ 0.99, and an AUC that ends up in a
table. Pathognomonic imaging signs (T2-FLAIR mismatch, the string sign, a halo sign) cause
it routinely: 100% specificity means an empty cell by construction.
python3 "${CLAUDE_SKILL_DIR}/scripts/check_separation.py" \
--data cohort.csv --outcome idh_mutant --auto --strict
COMPLETE_SEPARATION (an empty cell) and QUASI_SEPARATION (a cell below the sparsity
floor) both halt the plan. With two or more predictors the script also tests them jointly
(a linear combination can separate the outcome when no single predictor does); that test
needs scipy, and without it the report says the joint check was not run. The remedy is a design decision, made in the plan: Firth's
penalised likelihood keeps one model, while a two-stage rule — classify the sign-positive
cases directly, model only the sign-negative remainder — is usually the clinically meaningful
choice for a pathognomonic sign, because a sign-positive patient is already diagnosed.
Present the plan and wait for user approval before executing.
Generate and run a Python (preferred) or R script. Every number you report MUST come from its executed output, never estimated or hand-typed, because a number no script reproduces cannot be verified.
Start every script with a reproducibility header:
"""
Analysis: {description}
Date: {YYYY-MM-DD}
Random seed: 42
Python: {version}
Key packages: {package==version, ...}
"""
import numpy as np
import pandas as pd
np.random.seed(42)
np.random.seed(42) or set.seed(42).import matplotlib.pyplot as plt
style_path = os.path.join(os.environ.get('CLAUDE_SKILL_DIR', '.'), 'references/style/figure_style.mplstyle')
if os.path.exists(style_path):
plt.style.use(style_path)
Choose the summary and test from the design and the distribution, not a preliminary test:
|skewness| > 1 → median (IQR) with a
rank test, else mean (SD) (table-types/table1_demographics.md). Never gate on a Shapiro-Wilk or
KS P value: it flags trivial departures at large n, misses real ones at small n, and
test-then-test inflates the type I error (Rochon et al. 2012).sum(n per stratum) == unique N and
sum(events per stratum) == total events. A trend test on overlapping or non-exhaustive strata
is invalid. Print the per-stratum N/event table and the reconciliation (the analysis-side
mirror of /self-review check_cohort_arithmetic.py PARTITION_OVERLAP).max(0, lower). A zero-event
Wilson/score interval can print a negative or tiny-exponent bound (e.g. 3.47e-16) that is a
display artifact; report 0 (or 0.0%), and prefer an exact (Clopper-Pearson) interval for
zero/near-zero cells.After all analyses complete, save _analysis_outputs.md in the output directory, in the format
given in references/analysis_run_workflow.md, so
/make-figures and /write-paper can find the outputs without asking.
For prespecified binary predictions on independent units, use the bundled
scripts/run_analysis.py run workflow in that file: it embeds data/configuration/code/output
hashes, exact counts, metric-specific denominators and the reproduction command in the same
manifest (audit checks recorded versions without rewriting them; compare separates declared
context and numeric equality from byte drift). It does not select thresholds or establish study
validity, privacy clearance or reuse rights. Synthetic example:
python3 ${CLAUDE_SKILL_DIR}/scripts/demo_analysis_run.py --out demo-project.
Before reporting any script as final, lint every emitted .py/.R file:
python3 ${CLAUDE_SKILL_DIR}/scripts/check_generated_code.py {script.py} --strict
# or scan a whole output directory:
python3 ${CLAUDE_SKILL_DIR}/scripts/check_generated_code.py --code-dir {analysis_dir} --strict
Fix every Major before reporting the script: MISSING_SEED (randomness with no seed),
HARDCODED_DATA_LITERAL (hand-typed, table-shaped data instead of read_csv()/read.csv() +
subset), HARDCODED_ABS_PATH (non-portable and a PII risk), and INPLACE_SOURCE_OVERWRITE
(writing to the path read as input — never modify raw data; write derived outputs to a new
path). Fix the flags DEBUG_LEFTOVER and UNUSED_IMPORT when tidying.
Minor, Python only: API_DEFAULT_STUDENT_T (scipy's ttest_ind without equal_var is Student's
t; state Welch or the reason for Student) and API_DEFAULT_PENALIZED_OR (LogisticRegression
with none of penalty, C, l1_ratio, in a file that exponentiates a coef_). R's t.test()
is Welch by default.
After execution, generate manuscript-ready text from the script's output:
/search-lit confirmed; mark any other
[UNVERIFIED - NEEDS MANUAL CHECK].Report statistical results; leave judgments of clinical significance to the user. For designs this skill does not cover (adaptive or Bayesian trials, complex multilevel or causal-mediation models), say so and recommend biostatistician review before the results are reported.
statsmodels
TTestIndPower or scipy's nct); do not mix a normal approximation for some values with
exact-t for others. Do not report post-hoc (observed) power: it is a function of the P value
and adds nothing (Hoenig & Heisey 2001); report the CI of the effect instead./self-review treats a primary
metric without one as a defect. In the output CSV carry the interval as explicit columns
(estimate, ci_lower, ci_upper) or as one text column in est (lo–hi) form; never a point
estimate with no interval beside it. Round ORs/HRs/sHRs to 2 decimals and AUC/C-statistic
to 3.Whenever a primary result is a correlation, a standardized coefficient, a regression slope, an OR/HR/RR or a Cohen's d, also report it as a plain-language unit shift a non-statistician can act on — in addition to the effect size (rule 3), not instead of it. This applies to continuous exposure–outcome associations (Spearman's rho, Pearson's r, standardized slopes), to relative measures where the audience needs an absolute-risk feel, and to reader or expert-elicitation studies, clinical-utility framing, abstracts and figure captions.
delta_outcome = b * (x_p75 - x_p25). Keep the sign:
a negative b is a decrease.delta_outcome = beta * (x_p75 - x_p25) / SD_x; a
fully standardized beta is also multiplied by SD_outcome.b = r * SD_outcome / SD_x), which is the mean change per unit only if the relation is
linear; Spearman's rho converts to no change in outcome units. Fit the outcome on the
exposure and translate that slope (Theil-Sen if outliers are why Spearman was used); for a
monotonic but curved association, report the fitted outcome at x_p25 and at x_p75 instead.Output contract (clinical utility is a default, not an optional add-on):
references/table-standards/table-types/incremental_value.md and the make-figures
decision_curve exemplar (and render_core_figures.py for the rendered curve).install.packages() and wait for user confirmation.Code, tables, figures and console output are in English.
Before generating any publication table, load:
${CLAUDE_SKILL_DIR}/references/table-standards/journal-profiles/{journal}.yaml if a target
journal is known (it sets footnote markers, P and CI format, title and abbreviation order)${CLAUDE_SKILL_DIR}/references/table-standards/table-types/{type}.md for the table typeOutput formats (always generate all three): a CSV file (downstream use and archival), a
console markdown rendering (user review), and R gtsummary code (Word/LaTeX export). Read
${CLAUDE_SKILL_DIR}/references/table-standards/table-standards.md for the gtsummary pipeline
and the footnote placement order (general note, abbreviations, specific notes, probability
notes).
Validation checklist (run before finalizing any table):
PDF (vector) + PNG (300 DPI), styled with figure_style.mplstyle (Arial, colorblind-safe
palette); width 3.5 inches (single column) or 7.0 inches (double column); axis labels with
units.
Before generating code for any row, read the files listed for it (paths relative to
${CLAUDE_SKILL_DIR}); they carry the method rules this file does not repeat.
| Analysis type | Read before generating code | Template |
|---|---|---|
| Table 1 (demographics) | references/table-standards/table-types/table1_demographics.md | references/templates/table1_demographics.py |
| Diagnostic accuracy (Se, Sp, PPV, NPV, AUC) | references/analysis_guides/diagnostic_accuracy.md | references/templates/diagnostic_accuracy.py |
| Added value beyond a baseline model (ΔAUC, NRI/IDI, net benefit) | references/table-standards/table-types/incremental_value.md | — |
| Reader study (MRMC) | references/table-standards/table-types/reader_study.md | — |
| Prediction model (outputs a risk used for a decision) | references/analysis_guides/calibration.md | — |
| Inter-rater agreement (kappa, ICC, Bland–Altman) | references/analysis_guides/agreement_reliability.md, references/table-standards/table-types/agreement.md | references/templates/agreement_analysis.py |
| Meta-analysis (pairwise, single-arm proportion, DTA) | references/analysis_guides/meta_analysis.md | references/templates/dta_meta_analysis.R (DTA) |
| Network meta-analysis (≥3 interventions) | references/analysis_guides/network_meta_analysis.md | — |
| Health economic evaluation (CEA, CUA, budget impact) | references/analysis_guides/health_economic_evaluation.md | — |
| Survey/Likert (ordinal rating scales) | the Survey/Likert section below | references/templates/likert_summary.py |
| Survival, competing risks, interval-censored events | references/analysis_guides/survival.md, references/table-standards/table-types/survival_results.md | references/templates/survival_analysis.py (--cluster <id> for nested units) |
| Group comparison, correlation | references/analysis_guides/test_selection.md | — |
| Logistic or linear regression | references/analysis_guides/regression.md | references/templates/regression.py (regression_type = "logistic" or "linear") |
| Propensity score (PSM, IPTW, SIPTW, overlap weighting) | references/analysis_guides/propensity_score.md | references/templates/propensity_score.py |
| Survey-weighted (KNHANES, NHANES, KCHS) | references/analysis_guides/survey_weighted.md | references/templates/survey_weighted_analysis.py |
| NHIS claims-based studies (ICD-10 definitions) | references/analysis_guides/nhis_icd10_mapping.md | — |
| Repeated measures (LMM, GEE, RM ANOVA) | references/analysis_guides/repeated_measures.md; for missing data see references/analysis_guides/missing_data.md (an LMM already uses every observed outcome under MAR, so missing outcomes alone do not call for MICE) | references/templates/repeated_measures.py |
| Mediation | references/analysis_guides/mediation.md | — |
| Multiple testing, many-exposure scans (ExWAS/EWAS) | references/analysis_guides/multiplicity.md | — |
| Mendelian randomization | references/analysis_guides/mendelian_randomization.md | — |
| Polygenic risk score | references/analysis_guides/polygenic_risk_score.md | — |
| Burden of disease, decomposition, forecasting | references/analysis_guides/burden_decomposition_forecasting.md | — |
Comparing models: use a paired DeLong test for two separate scores on the same patients. For a nested "baseline + new term" model fitted and evaluated on the same data, DeLong is not the added-value test — test the term (likelihood ratio) and report ΔAUC with its CI.
The rules below have no separate guide.
(min+max) - x before computing the scale total or Cronbach's alpha. An un-recoded reverse
item produces a negative item-rest correlation and can make alpha negative — a coding bug,
not evidence of a multidimensional construct; do not defend it as one. likert_summary.py
prints per-item item-rest correlations, flags negative ones as reverse-code suspects, warns on
a negative alpha, and applies the recode with --reverse-items E3 .... To screen at cleaning
time, run python3 "${CLAUDE_SKILL_DIR}/../clean-data/scripts/check_reverse_coding.py".Applies to any multivariable adjustment (logistic / linear / Cox / propensity score / survey- weighted) with a dose/duration variable anchored to a categorical exposure (pack-years under smoking status, grams/week under alcohol use, cessation duration under former smoker):
pack_years is a structural
zero — known to be 0 by definition of the category, not missing. Imputing it (MICE/MNAR)
fabricates a dose for unexposed subjects and corrupts the exposure contrast. Before imputing a
dose/duration column, set the implied zero explicitly (IF status == 'never' THEN dose = 0)
and impute only the genuinely missing residual among the exposed. /clean-data flags never
rows with a NULL dose (scripts/check_structural_zero.py).Applies to any cross-sectional / single-visit outcome regression (temporal order is not observed). Select covariates causally, not statistically:
Skills that do similar work, with their headline benchmark lift.
From the same source and nearby in the catalogue.