Skip to main content Skills Marketplace コミュニティが作成したAIスキルを発見・探索
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/johnalbertini14-glitch/openclaw-skills --skill data-profilerコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
Zipをダウンロード ダウンロード中... name data-profiler description Profile construction data to understand characteristics, distributions, quality metrics, and patterns. Essential for data quality assessment and ETL planning. homepage https://datadrivenconstruction.io metadata {"openclaw":{"emoji":"🏷️","os":["darwin","linux","win32"],"homepage":"https://datadrivenconstruction.io","requires":{"bins":["python3"]}}}
Data Profiler for Construction
Overview
Analyze construction data to understand its characteristics, distributions, quality, and patterns. Essential for data quality assessment, ETL planning, and identifying data issues before they impact projects.
Business Case
Before using any construction data, you need to understand:
What data types are present
Distribution of values
Missing data patterns
Anomalies and outliers
Referential integrity issues
This skill profiles data to answer these questions and provides actionable insights.
Technical Implementation
from dataclasses import dataclass, field
from typing import List , Dict , Any , Optional , Tuple
pandas pd
numpy np
datetime datetime
json
:
name:
data_type:
inferred_type:
total_count:
null_count:
null_percentage:
unique_count:
uniqueness_ratio:
min_value: [ ] =
max_value: [ ] =
mean_value: [ ] =
median_value: [ ] =
std_dev: [ ] =
min_length: [ ] =
max_length: [ ] =
avg_length: [ ] =
top_values: [ [ , ]] = field(default_factory= )
common_patterns: [ ] = field(default_factory= )
quality_issues: [ ] = field(default_factory= )
:
source_name:
row_count:
column_count:
columns: [ColumnProfile]
duplicate_rows:
memory_usage:
profiled_at: datetime
quality_score:
recommendations: [ ]
:
CONSTRUCTION_PATTERNS = {
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
}
COLUMN_TYPE_HINTS = {
: [ , , , ],
: [ , , , , , ],
: [ , , , , , ],
: [ , , , ],
: [ , , , ],
: [ , , , , ],
: [ , , , , ],
}
( ):
.profiles: [ , DataProfile] = {}
( ) -> DataProfile:
columns = []
col df.columns:
col_profile = ._profile_column(df[col], col)
columns.append(col_profile)
duplicate_rows = (df) - (df.drop_duplicates())
memory_bytes = df.memory_usage(deep= ). ()
memory_bytes < :
memory_usage =
memory_bytes < ** :
memory_usage =
:
memory_usage =
quality_score = ._calculate_quality_score(columns)
recommendations = ._generate_recommendations(columns, df)
profile = DataProfile(
source_name=source_name,
row_count= (df),
column_count= (df.columns),
columns=columns,
duplicate_rows=duplicate_rows,
memory_usage=memory_usage,
profiled_at=datetime.now(),
quality_score=quality_score,
recommendations=recommendations
)
.profiles[source_name] = profile
profile
( ) -> ColumnProfile:
total_count = (series)
null_count = series.isnull(). ()
null_percentage = (null_count / total_count * ) total_count >
non_null = series.dropna()
unique_count = non_null.nunique()
uniqueness_ratio = unique_count / (non_null) (non_null) >
profile = ColumnProfile(
name=name,
data_type= (series.dtype),
inferred_type= ._infer_construction_type(series, name),
total_count=total_count,
null_count=null_count,
null_percentage= (null_percentage, ),
unique_count=unique_count,
uniqueness_ratio= (uniqueness_ratio, )
)
pd.api.types.is_numeric_dtype(series):
profile.min_value = (non_null. ()) (non_null) >
profile.max_value = (non_null. ()) (non_null) >
profile.mean_value = (non_null.mean()) (non_null) >
profile.median_value = (non_null.median()) (non_null) >
profile.std_dev = (non_null.std()) (non_null) >
(non_null) > profile.std_dev:
outliers = non_null[ (non_null - profile.mean_value) > * profile.std_dev]
(outliers) > :
profile.quality_issues.append( )
(hint name.lower() hint [ , , , ]):
negatives = (non_null < ). ()
negatives > :
profile.quality_issues.append( )
pd.api.types.is_object_dtype(series) pd.api.types.is_string_dtype(series):
str_series = non_null.astype( )
lengths = str_series. . ()
profile.min_length = (lengths. ()) (lengths) >
profile.max_length = (lengths. ()) (lengths) >
profile.avg_length = (lengths.mean()) (lengths) >
profile.common_patterns = ._detect_patterns(str_series)
(non_null) > :
value_counts = non_null.value_counts().head( )
profile.top_values = ( (value_counts.index.tolist(), value_counts.values.tolist()))
null_percentage > :
profile.quality_issues.append( )
uniqueness_ratio == total_count > :
profile.quality_issues.append( )
uniqueness_ratio < unique_count > :
profile.quality_issues.append( )
profile
( ) -> :
name_lower = name.lower()
type_name, hints .COLUMN_TYPE_HINTS.items():
(hint name_lower hint hints):
type_name
non_null = series.dropna().astype( )
(non_null) == :
sample = non_null.head( )
pattern_name, pattern .CONSTRUCTION_PATTERNS.items():
matches = sample. . (pattern, na= ). ()
matches / (sample) > :
pattern_name
pd.api.types.is_numeric_dtype(series):
pd.api.types.is_datetime64_any_dtype(series):
:
( ) -> [ ]:
patterns_found = []
sample = str_series.head( )
pattern_name, pattern .CONSTRUCTION_PATTERNS.items():
matches = sample. . (pattern, na= ). ()
matches / (sample) > :
patterns_found.append( )
patterns_found[: ]
( ) -> :
columns:
scores = []
col columns:
col_score =
col_score -= (col.null_percentage, )
col_score -= (col.quality_issues) *
scores.append( (col_score, ))
( (scores) / (scores), )
( ) -> [ ]:
recommendations = []
high_null = [c c columns c.null_percentage > ]
high_null:
recommendations.append(
)
col columns:
col.name.lower() col.uniqueness_ratio < :
recommendations.append(
)
col columns:
col.inferred_type [ , ] col.data_type == :
recommendations.append(
)
col columns:
col.inferred_type == col.data_type == :
recommendations.append(
)
recommendations
( ) -> :
{
: profile.source_name,
: profile.row_count,
: profile.column_count,
: profile.duplicate_rows,
: profile.memory_usage,
: profile.profiled_at.isoformat(),
: profile.quality_score,
: profile.recommendations,
: [
{
: c.name,
: c.data_type,
: c.inferred_type,
: c.null_percentage,
: c.unique_count,
: c.quality_issues,
: c.top_values[: ]
}
c profile.columns
]
}
( ) -> :
report = [ , ]
report.append( )
report.append( )
report.append( )
report.append( )
report.append( )
report.append( )
report.append( )
report.append( )
report.append( )
profile.recommendations:
report.append( )
rec profile.recommendations:
report.append( )
report.append( )
report.append( )
report.append( )
report.append( )
report.append( )
col profile.columns:
issues = (col.quality_issues)
report.append(
)
report.append( )
report.append( )
col profile.columns:
report.append( )
report.append( )
report.append( )
report.append( )
col.min_value :
report.append( )
report.append( )
col.min_length :
report.append( )
col.top_values:
report.append( )
col.common_patterns:
report.append( )
col.quality_issues:
report.append( )
.join(report)
( ) -> :
comparison = {
: [profile1.source_name, profile2.source_name],
: profile2.row_count - profile1.row_count,
: profile2.quality_score - profile1.quality_score,
: [],
: [],
: [],
: []
}
cols1 = {c.name: c c profile1.columns}
cols2 = {c.name: c c profile2.columns}
comparison[ ] = [n n cols2 n cols1]
comparison[ ] = [n n cols1 n cols2]
name cols1:
name cols2:
c1, c2 = cols1[name], cols2[name]
c1.data_type != c2.data_type:
comparison[ ].append({
: name,
: c1.data_type,
: c2.data_type
})
null_change = c2.null_percentage - c1.null_percentage
(null_change) > :
comparison[ ].append({
: name,
: null_change
})
comparison
import
as
import
as
from
import
import
@dataclass
class
ColumnProfile
str
str
str
int
int
float
int
float
Optional
float
None
Optional
float
None
Optional
float
None
Optional
float
None
Optional
float
None
Optional
int
None
Optional
int
None
Optional
float
None
List
Tuple
Any
int
list
List
str
list
List
str
list
@dataclass
class
DataProfile
str
int
int
List
int
str
float
List
str
class
ConstructionDataProfiler
"""Profile construction data for quality and characteristics."""
'csi_code'
r'^\d{2}\s?\d{2}\s?\d{2}$'
'project_id'
r'^[A-Z]{2,4}[-_]?\d{3,6}$'
'cost_code'
r'^\d{2}[-.]?\d{2,4}$'
'wbs'
r'^[\d.]+$'
'phone'
r'^\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$'
'email'
r'^[\w.-]+@[\w.-]+\.\w+$'
'date_iso'
r'^\d{4}-\d{2}-\d{2}'
'date_us'
r'^\d{1,2}/\d{1,2}/\d{2,4}$'
'currency'
r'^\$?[\d,]+\.?\d{0,2}$'
'percentage'
r'^\d+\.?\d*%?$'
'project'
'project_id'
'project_name'
'proj'
'job'
'cost'
'cost'
'amount'
'price'
'total'
'budget'
'actual'
'date'
'date'
'start'
'finish'
'end'
'created'
'modified'
'quantity'
'qty'
'quantity'
'count'
'units'
'csi'
'csi'
'division'
'masterformat'
'spec'
'location'
'location'
'area'
'zone'
'floor'
'level'
'person'
'owner'
'manager'
'superintendent'
'foreman'
'contact'
def
__init__
self
self
Dict
str
def
profile_dataframe
self, df: pd.DataFrame, source_name: str
"""Profile a pandas DataFrame."""
for
in
self
len
len
True
sum
if
1024
f"{memory_bytes} B"
elif
1024
2
f"{memory_bytes/1024 :.1 f} KB"
else
f"{memory_bytes/1024 **2 :.1 f} MB"
self
self
len
len
self
return
def
_profile_column
self, series: pd.Series, name: str
"""Profile a single column."""
len
sum
100
if
0
else
0
len
if
len
0
else
0
str
self
round
2
round
4
if
float
min
if
len
0
else
None
float
max
if
len
0
else
None
float
if
len
0
else
None
float
if
len
0
else
None
float
if
len
1
else
None
if
len
10
and
abs
3
if
len
0
f"{len (outliers)} potential outliers detected"
if
any
in
for
in
'cost'
'amount'
'price'
'total'
0
sum
if
0
f"{negatives} negative values in cost column"
elif
or
str
str
len
int
min
if
len
0
else
None
int
max
if
len
0
else
None
float
if
len
0
else
None
self
if
len
0
5
list
zip
if
50
"High null rate (>50%)"
if
1.0
and
100
"All unique values - possible ID column"
if
0.01
and
1
"Low cardinality - possible category"
return
def
_infer_construction_type
self, series: pd.Series, name: str
str
"""Infer construction-specific data type."""
for
in
self
if
any
in
for
in
return
str
if
len
0
return
"unknown"
100
for
in
self
str
match
False
sum
if
len
0.8
return
if
return
"numeric"
elif
return
"datetime"
else
return
"text"
def
_detect_patterns
self, str_series: pd.Series
List
str
"""Detect common patterns in string data."""
1000
for
in
self
str
match
False
sum
if
len
0.1
f"{pattern_name} ({matches/len (sample):.0 %} )"
return
3
def
_calculate_quality_score
self, columns: List [ColumnProfile]
float
"""Calculate overall data quality score (0-100)."""
if
not
return
0.0
for
in
100
min
50
len
10
max
0
return
round
sum
len
1
def
_generate_recommendations
self, columns: List [ColumnProfile], df: pd.DataFrame
List
str
"""Generate recommendations based on profile."""
for
in
if
30
if
f"Review {len (high_null)} columns with >30% null values: "
f"{', ' .join(c.name for c in high_null[:3 ])} "
for
in
if
'id'
in
and
1.0
f"Column '{col.name} ' appears to be an ID but has duplicate values"
for
in
if
in
'date_iso'
'date_us'
and
'object'
f"Convert '{col.name} ' to datetime type for better analysis"
for
in
if
'currency'
and
'object'
f"Convert '{col.name} ' to numeric type (remove $ and commas)"
return
def
profile_to_dict
self, profile: DataProfile
Dict
"""Convert profile to dictionary for JSON export."""
return
'source_name'
'row_count'
'column_count'
'duplicate_rows'
'memory_usage'
'profiled_at'
'quality_score'
'recommendations'
'columns'
'name'
'data_type'
'inferred_type'
'null_percentage'
'unique_count'
'quality_issues'
'top_values'
3
for
in
def
generate_profile_report
self, profile: DataProfile
str
"""Generate markdown profile report."""
f"# Data Profile: {profile.source_name} "
""
f"**Profiled At:** {profile.profiled_at.strftime('%Y-%m-%d %H:%M' )} "
f"**Quality Score:** {profile.quality_score} /100"
""
"## Summary"
f"- **Rows:** {profile.row_count:,} "
f"- **Columns:** {profile.column_count} "
f"- **Duplicate Rows:** {profile.duplicate_rows:,} "
f"- **Memory Usage:** {profile.memory_usage} "
""
if
"## Recommendations"
for
in
f"- {rec} "
""
"## Column Details"
""
"| Column | Type | Inferred | Nulls | Unique | Issues |"
"|--------|------|----------|-------|--------|--------|"
for
in
len
f"| {col.name} | {col.data_type} | {col.inferred_type} | "
f"{col.null_percentage:.1 f} % | {col.unique_count:,} | {issues} |"
""
"## Detailed Column Profiles"
for
in
f"\n### {col.name} "
f"- **Type:** {col.data_type} (inferred: {col.inferred_type} )"
f"- **Nulls:** {col.null_count:,} ({col.null_percentage:.1 f} %)"
f"- **Unique Values:** {col.unique_count:,} ({col.uniqueness_ratio:.1 %} )"
if
is
not
None
f"- **Range:** {col.min_value:,.2 f} to {col.max_value:,.2 f} "
f"- **Mean:** {col.mean_value:,.2 f} , Median: {col.median_value:,.2 f} "
if
is
not
None
f"- **Length:** {col.min_length} to {col.max_length} (avg: {col.avg_length:.1 f} )"
if
f"- **Top Values:** {col.top_values[:3 ]} "
if
f"- **Patterns:** {', ' .join(col.common_patterns)} "
if
f"- **Issues:** {', ' .join(col.quality_issues)} "
return
"\n"
def
compare_profiles
self, profile1: DataProfile, profile2: DataProfile
Dict
"""Compare two profiles to detect schema changes or data drift."""
'profiles'
'row_count_change'
'quality_change'
'new_columns'
'removed_columns'
'type_changes'
'null_rate_changes'
for
in
for
in
'new_columns'
for
in
if
not
in
'removed_columns'
for
in
if
not
in
for
in
if
in
if
'type_changes'
'column'
'from'
'to'
if
abs
10
'null_rate_changes'
'column'
'change'
return
Quick Start import pandas as pd
df = pd.read_excel("project_costs.xlsx" )
profiler = ConstructionDataProfiler()
profile = profiler.profile_dataframe(df, "Project Costs 2025" )
report = profiler.generate_profile_report(profile)
print (report)
profile_dict = profiler.profile_to_dict(profile)
with open ("profile.json" , "w" ) as f:
json.dump(profile_dict, f, indent=2 )
old_profile = profiler.profile_dataframe(old_df, "Project Costs 2024" )
comparison = profiler.compare_profiles(old_profile, profile)
print (f"Quality changed by: {comparison['quality_change' ]} " )
Common Use Cases
Pre-ETL Analysis : Profile source data before building pipelines
Quality Monitoring : Track data quality over time
Schema Validation : Detect unexpected changes in data structure
Anomaly Detection : Find outliers and data quality issues
Dependencies
Resources
Data Profiling Best Practices : DAMA DMBOK
Construction Data Standards : CSI MasterFormat, UniFormat