Your manager wants the number of A-grade accounts. The dashboard already has a priorityFlag checkbox, and using it would make the report pleasantly quick.
It would also answer a different question: priorityFlag is true for grade A, funding in the last 180 days, or an executive change in the last 30 days.
The checkbox is a union.
Follow the routes into priority
The eligibility rule becomes easier to defend when you draw it:
Grade A ------------------\
Recent funding ------------ OR ---> priorityFlag
Recent executive change --/
An account can qualify through an event without having the top grade. It can also satisfy overlapping conditions, so adding the individual rule counts will double-count accounts that qualify through several routes.
For the requested A-grade total, count grade == "A". For operational priority, count the emitted flag. For an explanation of priority, retain separate eligibility indicators.
That distinction matters when your manager asks whether the account list improved. A change in priority membership could reflect event timing rather than stronger scores. A checkbox is remarkably efficient at hiding the difference.
Choose a company-run population
The Actor returns a graded company record with ranking fields and supporting evidence. When every enabled source fails, it returns a company_error record instead, carrying the company name, source notes and run timestamp.
Those errors belong in coverage reporting. They don't belong in a scored-company denominator.
companyName preserves the supplied name. It isn't a stable identity key. If you concatenate exports, you need an account identity and a run selection before calling the row count an account count.
This Example input requests detailed evidence and keeps the score threshold open:
import os
import duckdb
import pandas as pd
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run_input = {
"mode": "score",
"companiesText": "Example Company",
"outputFormat": "full",
"lookbackDays": 180,
"minScore": 0,
}
run = client.actor(
"dataspryva/company-intent-scorer-hiring-funding-and-news"
).call(run_input=run_input)
items = list(
client.dataset(run["defaultDatasetId"]).iterate_items()
)
Iterating through the dataset avoids treating a page of items as the complete export. Materialising the iterator here gives pandas the returned records for the audit.
Keep the input alongside your results. Collection settings explain the population; the returned fields don't preserve every decision that produced it.
Separate stored fields from collection settings
The fields relevant to this audit mix ranking values, nested evidence and input settings:
| Field | Value and pandas treatment | Omission or analysis trap |
|---|---|---|
priorityFlag |
Logical eligibility value. Use nullable boolean; accept actual booleans only. |
Absent from company-error records. Malformed values must remain unknown rather than becoming truthy. |
grade |
A–D label. Keep recognised values as nullable strings. A is 70+, B is 45–69, C is 20–44 and D is below 20. | Absent from company errors. Don't silently replace an unrecognised label using the score. |
score |
Numeric intent score from 0 to 100. Use nullable Float64 without assuming an integer-only representation. |
A mean excludes errors and missing or invalid scores. Its denominator needs a label. |
runTimestamp |
ISO 8601 run timestamp. Parse into a timezone-aware UTC timestamp. | An unusable timestamp prevents a reproducible event-age comparison. |
fundingSignal |
Nested funding evidence, added in full output when available. Check that the value is an object before extracting fields. | Funding requires verifiable evidence. Omission doesn't establish that the company raised nothing. |
fundingSignal.announcedDate |
Funding date; no fixed representation or precision is specified. Preserve the raw value and parse cautiously. | An absent or unusable date leaves the recency test unresolved. |
fundingSignal.daysSinceRound |
Reported round age; no fixed scalar representation is specified. Attempt numeric conversion into nullable Float64. |
Don't assume its rounding or reference clock matches your calculation. |
execChangeSignal |
Detailed executive-change evidence in full output. Retain the raw value for inspection. | Presence alone doesn't establish a qualifying event date. This loader leaves its recency indicator unknown. |
lookbackDays |
Integer collection input. Default 90; permitted range 7–365. Applies to funding, news and executive changes. | It isn't a returned event age or a synonym for either priority cutoff. |
outputFormat |
String input, default "compact"; "full" adds detailed signal objects and the score breakdown. |
Missing detail in a compact export is an export choice before it is an evidence finding. |
The Actor omits empty fields at every depth. Absence therefore needs interpretation before it becomes a value.
Disabling a scored signal also normalises the remaining weights. Compare score distributions across runs only after checking that their signal selections match; the same numeric scale doesn't guarantee the same measurement.
Missing ages change the average
A naïve boolean cast turns a non-empty string into True. That's an especially unhelpful repair for a field whose meaning you're trying to audit.
This cell retains raw values, adds omitted columns explicitly and builds nullable analytical fields:
columns = [
"companyName", "type", "grade", "score", "priorityFlag",
"runTimestamp", "fundingSignal", "execChangeSignal",
]
raw = pd.DataFrame(items).reindex(columns=columns)
company_errors = raw["type"].eq("company_error")
df = raw[["companyName"]].astype("string")
df["grade"] = raw["grade"].astype("string").where(
raw["grade"].isin(["A", "B", "C", "D"])
)
numeric_score = pd.to_numeric(raw["score"], errors="coerce")
df["score"] = numeric_score.where(
numeric_score.between(0, 100)
).astype("Float64")
df["priorityFlag"] = raw["priorityFlag"].map(
lambda value: value if isinstance(value, bool) else pd.NA
).astype("boolean")
funding_objects = raw["fundingSignal"].map(
lambda value: value if isinstance(value, dict) else {}
)
numeric_age = pd.to_numeric(
funding_objects.map(lambda signal: signal.get("daysSinceRound")),
errors="coerce",
)
df["daysSinceRound"] = numeric_age.where(
numeric_age.ge(0)
).astype("Float64")
Numeric conversion accepts numeric strings. It deliberately doesn't strip arbitrary text or invent a unit convention for an unspecified format.
An average daysSinceRound now describes records with usable reported round ages. It doesn't describe all accounts or all flagged accounts.
Funding visibility is selective: the Actor reports funding it can verify from a filing or an entity-matched press headline with a real amount or round. The average therefore describes observable funding evidence within the returned population, rather than the funding history of your whole account list.
Filling missing ages with zero would turn “age unavailable” into “just raised”. Missing data has enough problems without receiving a promotion.
Freeze the clock before testing funding
Recalculating recency against today's date makes an unchanged export produce a changing answer. Anchor the comparison to each record's runTimestamp.
This parser accepts timestamp strings with an explicit timezone and converts them to UTC. Values without a timezone remain unresolved in this audit rather than acquiring an invented one:
def parse_utc(value):
if not isinstance(value, str):
return pd.NaT
stamp = pd.to_datetime(value, errors="coerce")
if pd.isna(stamp) or stamp.tzinfo is None:
return pd.NaT
return stamp.tz_convert("UTC")
df["runTimestamp"] = pd.to_datetime(
raw["runTimestamp"].map(parse_utc), utc=True
)
df["announcedDate"] = pd.to_datetime(
funding_objects.map(
lambda signal: parse_utc(signal.get("announcedDate"))
),
utc=True,
)
Keep daysSinceRound beside the timestamp-derived test. If they disagree, preserve the discrepancy until you can explain the reference clock and date precision.
The next cell separates the eligibility routes. Its funding test uses elapsed time and an inclusive cutoff as audit conventions; internal boundary rounding isn't specified.
The executive route stays unknown here because this loader doesn't extract executive event dates. We retain execChangeSignal in raw for inspection. Treating the object's presence as proof of recency would manufacture the reason we're trying to explain.
def visible_recent(date, reference, days):
if pd.isna(date) or pd.isna(reference) or date > reference:
return pd.NA
return bool(reference - date <= pd.Timedelta(days=days))
df["grade_a"] = df["grade"].eq("A").astype("boolean")
df["funding_recent"] = pd.Series(
[
visible_recent(date, reference, 180)
for date, reference in zip(
df["announcedDate"], df["runTimestamp"]
)
],
index=df.index,
dtype="boolean",
)
df["executive_recent"] = pd.Series(
pd.NA, index=df.index, dtype="boolean"
)
df["visible_union"] = (
df["grade_a"] | df["funding_recent"] | df["executive_recent"]
)
Here, a false funding indicator means the supplied dated funding evidence falls outside the window. It doesn't certify that the company's entire event history has been observed.
Nullable boolean logic preserves a useful distinction: a known qualifying route establishes eligibility even when another route is unknown. Without a known qualifying route, this partial reconstruction remains unresolved.
The funding eligibility horizon exceeds the default collection window. A longer eligibility rule can't recover an event that collection didn't supply.
Our chosen input widens collection, but that doesn't guarantee complete coverage. This is a diagnostic comparison with the emitted flag, not an exact reconstruction of its internal processing.
Count the intersections, then the gaps
The record from Company Intent Scorer: Hiring, Funding & News Signals lets you distinguish the emitted classification from the evidence you can independently evaluate.
The cross-tab below intersects priority values with recognised grades. The membership table retains overlapping routes and unknowns, while each average carries its contributing record count:
audit = df.loc[~company_errors].copy()
grade_by_priority = pd.crosstab(
audit["grade"].fillna("unresolved"),
audit["priorityFlag"].astype("string").fillna("unresolved"),
dropna=False,
)
rule_columns = ["grade_a", "funding_recent", "executive_recent"]
membership_counts = (
audit.groupby(rule_columns, dropna=False)
.size()
.rename("records")
)
population = pd.Series({
"returned_records": len(raw),
"company_error_records": int(company_errors.sum()),
"non_error_records": len(audit),
})
score_summary = audit["score"].agg(["mean", "count"])
reported_round_age_summary = audit["daysSinceRound"].agg(
["mean", "count"]
)
A priority share needs a declared denominator too. “Among records with a usable flag” and “among returned non-error records” answer different questions when flags are unresolved.
DuckDB can separate the requested grade count, the operational flag count and the unresolved audit population:
count_audit = duckdb.sql("""
SELECT
count(*) AS non_error_records,
count(*) FILTER (WHERE grade_a IS TRUE) AS grade_a_records,
count(*) FILTER (
WHERE priorityFlag IS TRUE
) AS priority_records,
count(*) FILTER (
WHERE priorityFlag IS TRUE AND grade_a IS TRUE
) AS priority_grade_a_records,
count(*) FILTER (
WHERE priorityFlag IS TRUE AND grade_a IS FALSE
) AS priority_other_known_grade_records,
count(*) FILTER (
WHERE priorityFlag IS TRUE AND grade_a IS NULL
) AS priority_unknown_grade_records,
count(*) FILTER (
WHERE priorityFlag IS NULL
) AS unknown_priority_records,
count(*) FILTER (
WHERE visible_union IS NULL
) AS unresolved_union_records,
count(*) FILTER (
WHERE visible_union IS NOT NULL
AND priorityFlag IS NOT NULL
AND visible_union <> priorityFlag
) AS known_disagreements
FROM audit
""").df()
Because the executive route remains unknown, the partial reconstruction can't establish ineligibility. It can establish a visible qualifying reason, or leave the reason unresolved.
A known disagreement deserves inspection of the original evidence and collection settings. It doesn't, by itself, prove an incorrect flag.
These counts also describe the returned population. A higher minScore removes lower-scored companies while keeping error records. Monitor mode can omit unchanged companies, making its export a change population rather than a complete account snapshot.
A dashboard label should carry that scope. “Returned A-grade accounts” is defensible where “A-grade accounts in the market” isn't.
Leave buying intent unproven
Public signals and their attached grades don't establish an account's actual budget, purchasing authority or intention to buy.
Nor does an unresolved priority reason establish that no qualifying event occurred. It establishes a limit on what this audit can explain.
An unresolved reason belongs in the report, not inside a reassuring checkbox.
Top comments (0)