{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":108394,"databundleVersionId":13172641,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Great Daxinzhuang Pottery Puzzle Challenge - Exploratory Data Analysis\n# 大辛庄陶片拼合AI挑战赛 - 数据探索分析\n\n\"\"\"\nThis notebook provides comprehensive exploratory data analysis for the Daxinzhuang \nPottery Puzzle Challenge. It includes automatic data discovery to handle different\nKaggle directory structures.\n\nCompetition Overview:\n- Goal: Develop AI models to match ~20,000 pottery fragments from Shang Dynasty\n- Primary Tasks: (1) Edge matching for reassembly, (2) 3D vessel reconstruction\n- Evaluation: Matching accuracy (70%), completeness (15%), innovation (15%)\n\"\"\"\n\nimport os\nimport pandas as pd\nimport numpy as np\nimport cv2\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom collections import Counter\nfrom tqdm.notebook import tqdm\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Configure visualization settings\nplt.rcParams['figure.figsize'] = (12, 8)\nplt.rcParams['font.size'] = 12\nsns.set_style(\"whitegrid\")\n\n# =============================================================================\n# Section 0: Data Discovery and Setup\n# =============================================================================\n\nprint(\"=\" * 80)\nprint(\"SECTION 0: DATA DISCOVERY AND SETUP\")\nprint(\"=\" * 80)\n\ndef find_competition_data():\n    \"\"\"\n    Automatically discover the competition data location on Kaggle.\n    Returns paths to CSV file, image directory, and reference directory.\n    \"\"\"\n    possible_base_paths = [\n        '/kaggle/input/h690',\n        '/kaggle/input/great-daxinzhuang-pottery-puzzle-challenge',\n        '/kaggle/input',\n        '../input/h690',\n        '../input',\n        '.'\n    ]\n    \n    csv_file = None\n    image_dir = None\n    reference_dir = None\n    base_dir = None\n    \n    print(\"Searching for competition data...\")\n    \n    for base_path in possible_base_paths:\n        if os.path.exists(base_path):\n            print(f\"\\nChecking: {base_path}\")\n            \n            # List contents\n            try:\n                contents = os.listdir(base_path)\n                print(f\"  Contents: {contents[:5]}{'...' if len(contents) > 5 else ''}\")\n                \n                # Look for the CSV file\n                for item in contents:\n                    if item == 'jd_sherds_info.csv':\n                        csv_file = os.path.join(base_path, item)\n                        base_dir = base_path\n                        print(f\"  ✓ Found CSV file: {csv_file}\")\n                    elif 'jd_sherds_info' in item and item.endswith('.csv'):\n                        csv_file = os.path.join(base_path, item)\n                        base_dir = base_path\n                        print(f\"  ✓ Found CSV file: {csv_file}\")\n                \n                # Look for directories\n                for item in contents:\n                    item_path = os.path.join(base_path, item)\n                    if os.path.isdir(item_path):\n                        if 'sherd' in item.lower() and 'image' in item.lower():\n                            image_dir = item_path\n                            print(f\"  ✓ Found image directory: {image_dir}\")\n                        elif 'reference' in item.lower():\n                            reference_dir = item_path\n                            print(f\"  ✓ Found reference directory: {reference_dir}\")\n                \n                if csv_file:\n                    break\n                    \n            except Exception as e:\n                print(f\"  Error accessing {base_path}: {e}\")\n    \n    # If main directories not found, search subdirectories\n    if not csv_file and base_dir:\n        print(\"\\nSearching subdirectories...\")\n        for root, dirs, files in os.walk('/kaggle/input'):\n            for file in files:\n                if file == 'jd_sherds_info.csv':\n                    csv_file = os.path.join(root, file)\n                    base_dir = root\n                    print(f\"Found CSV in: {csv_file}\")\n                    break\n            if csv_file:\n                break\n    \n    # Set default directories based on where CSV was found\n    if csv_file and not image_dir:\n        potential_image_dir = os.path.join(os.path.dirname(csv_file), 'sherd_images')\n        if os.path.exists(potential_image_dir):\n            image_dir = potential_image_dir\n            print(f\"Found image directory: {image_dir}\")\n    \n    if csv_file and not reference_dir:\n        potential_ref_dir = os.path.join(os.path.dirname(csv_file), 'reference_shapes')\n        if os.path.exists(potential_ref_dir):\n            reference_dir = potential_ref_dir\n            print(f\"Found reference directory: {reference_dir}\")\n    \n    return csv_file, image_dir, reference_dir\n\n# Discover data locations\nCSV_PATH, IMAGE_DIR, REFERENCE_DIR = find_competition_data()\n\nif not CSV_PATH:\n    print(\"\\n\" + \"!\" * 60)\n    print(\"ERROR: Could not find the competition data!\")\n    print(\"!\" * 60)\n    print(\"\\nPlease ensure you have:\")\n    print(\"1. Added the competition dataset to your notebook\")\n    print(\"2. The dataset contains 'jd_sherds_info.csv'\")\n    print(\"\\nTo add the dataset:\")\n    print(\"- Click 'Add data' in the notebook\")\n    print(\"- Search for 'h690' or 'Daxinzhuang'\")\n    print(\"- Add the competition dataset\")\n    raise FileNotFoundError(\"Competition data not found. Please add the dataset to your notebook.\")\n\nprint(\"\\n\" + \"=\" * 40)\nprint(\"Data Successfully Located!\")\nprint(\"=\" * 40)\nprint(f\"CSV Path: {CSV_PATH}\")\nprint(f\"Image Directory: {IMAGE_DIR or 'Not found'}\")\nprint(f\"Reference Directory: {REFERENCE_DIR or 'Not found'}\")\n\n# =============================================================================\n# Section 1: Data Structure and File Organization\n# =============================================================================\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"SECTION 1: DATA STRUCTURE AND FILE ORGANIZATION\")\nprint(\"=\" * 80)\n\ndef examine_directory_structure():\n    \"\"\"Analyze the competition data directory structure\"\"\"\n    \n    print(\"\\nData Directory Structure:\")\n    print(\"-\" * 40)\n    \n    # Count files in each directory\n    image_files = []\n    reference_files = []\n    \n    if IMAGE_DIR and os.path.exists(IMAGE_DIR):\n        image_files = os.listdir(IMAGE_DIR)\n        print(f\"✓ sherd_images/ ({len(image_files):,} files)\")\n    else:\n        print(\"✗ sherd_images/ directory not found\")\n    \n    if REFERENCE_DIR and os.path.exists(REFERENCE_DIR):\n        reference_files = os.listdir(REFERENCE_DIR)\n        print(f\"✓ reference_shapes/ ({len(reference_files):,} files)\")\n    else:\n        print(\"✗ reference_shapes/ directory not found\")\n    \n    # Analyze file types\n    if image_files:\n        image_extensions = Counter([f.split('.')[-1].lower() for f in image_files])\n        print(f\"\\nImage file types in sherd_images:\")\n        for ext, count in image_extensions.most_common():\n            print(f\"  - .{ext}: {count:,} files\")\n    \n    return image_files, reference_files\n\nimage_files, reference_files = examine_directory_structure()\n\n# =============================================================================\n# Section 2: Metadata Analysis\n# =============================================================================\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"SECTION 2: METADATA ANALYSIS\")\nprint(\"=\" * 80)\n\n# Load metadata\ntry:\n    metadata_df = pd.read_csv(CSV_PATH)\n    print(f\"Successfully loaded metadata from: {CSV_PATH}\")\nexcept Exception as e:\n    print(f\"Error loading CSV file: {e}\")\n    raise\n\nprint(\"\\nMetadata Overview:\")\nprint(\"-\" * 40)\nprint(f\"Total records: {len(metadata_df):,}\")\nprint(f\"Columns: {list(metadata_df.columns)}\")\nprint(f\"\\nData types:\")\nprint(metadata_df.dtypes)\n\n# Display sample records\nprint(\"\\nSample Records:\")\nprint(metadata_df.head(10))\n\n# Analyze unique values in each column\nprint(\"\\nUnique Values Analysis:\")\nprint(\"-\" * 40)\nfor col in metadata_df.columns:\n    unique_count = metadata_df[col].nunique()\n    null_count = metadata_df[col].isnull().sum()\n    print(f\"\\n{col}:\")\n    print(f\"  - Unique values: {unique_count:,}\")\n    print(f\"  - Null values: {null_count:,}\")\n    if unique_count <= 20:\n        print(f\"  - Values: {sorted(metadata_df[col].dropna().unique())}\")\n\n# Analyze sherd distribution\nunique_sherds = metadata_df['sherd_id'].nunique()\nprint(f\"\\nTotal unique pottery sherds: {unique_sherds:,}\")\nprint(f\"Average images per sherd: {len(metadata_df) / unique_sherds:.2f}\")\n\n# =============================================================================\n# Section 3: Stratification and Context Analysis\n# =============================================================================\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"SECTION 3: STRATIFICATION AND CONTEXT ANALYSIS\")\nprint(\"=\" * 80)\n\n# Analyze excavation units (stratigraphic layers)\nunit_distribution = metadata_df['unit'].value_counts().sort_index()\n\nplt.figure(figsize=(14, 6))\n\nplt.subplot(1, 2, 1)\nunit_distribution.plot(kind='bar', color='steelblue', alpha=0.8)\nplt.title('Distribution of Sherds by Excavation Unit', fontsize=14, fontweight='bold')\nplt.xlabel('Excavation Unit')\nplt.ylabel('Number of Images')\nplt.xticks(rotation=45)\nplt.grid(True, alpha=0.3)\n\n# Analyze pottery types\nplt.subplot(1, 2, 2)\ntype_distribution = metadata_df['type'].value_counts().head(15)\ntype_distribution.plot(kind='barh', color='darkgreen', alpha=0.8)\nplt.title('Top 15 Pottery Types', fontsize=14, fontweight='bold')\nplt.xlabel('Number of Images')\nplt.ylabel('Pottery Type')\nplt.grid(True, alpha=0.3)\n\nplt.tight_layout()\nplt.show()\n\n# Analyze pottery parts\nprint(\"\\nPottery Part Distribution:\")\npart_distribution = metadata_df['part'].value_counts()\nprint(part_distribution)\n\n# Cross-tabulation analysis\nprint(\"\\nCross-tabulation: Unit vs Image Side\")\nunit_side_crosstab = pd.crosstab(metadata_df['unit'], metadata_df['image_side'])\nprint(unit_side_crosstab)\n\n# =============================================================================\n# Section 4: Image Analysis (if images are available)\n# =============================================================================\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"SECTION 4: IMAGE ANALYSIS\")\nprint(\"=\" * 80)\n\nif IMAGE_DIR and os.path.exists(IMAGE_DIR) and image_files:\n    def analyze_sample_images(sample_size=100):\n        \"\"\"Analyze properties of sample images\"\"\"\n        \n        # Sample random images\n        sample_metadata = metadata_df.sample(n=min(sample_size, len(metadata_df)), random_state=42)\n        \n        image_stats = {\n            'widths': [],\n            'heights': [],\n            'channels': [],\n            'file_sizes': [],\n            'mean_brightness': [],\n            'std_brightness': []\n        }\n        \n        valid_images = 0\n        missing_images = 0\n        \n        print(f\"Analyzing {len(sample_metadata)} sample images...\")\n        \n        for _, row in tqdm(sample_metadata.iterrows(), total=len(sample_metadata)):\n            image_path = os.path.join(IMAGE_DIR, f\"{row['image_id']}.jpg\")\n            \n            if os.path.exists(image_path):\n                # Get file size\n                file_size = os.path.getsize(image_path) / 1024  # KB\n                image_stats['file_sizes'].append(file_size)\n                \n                # Load and analyze image\n                img = cv2.imread(image_path)\n                if img is not None:\n                    valid_images += 1\n                    h, w, c = img.shape\n                    image_stats['heights'].append(h)\n                    image_stats['widths'].append(w)\n                    image_stats['channels'].append(c)\n                    \n                    # Calculate brightness statistics\n                    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n                    image_stats['mean_brightness'].append(np.mean(gray))\n                    image_stats['std_brightness'].append(np.std(gray))\n            else:\n                missing_images += 1\n        \n        print(f\"\\nSuccessfully analyzed {valid_images} images\")\n        print(f\"Missing images: {missing_images}\")\n        \n        if valid_images > 0:\n            # Display statistics\n            print(\"\\nImage Statistics:\")\n            print(\"-\" * 40)\n            if image_stats['widths']:\n                print(f\"Image dimensions: {int(np.mean(image_stats['widths']))} x {int(np.mean(image_stats['heights']))} pixels\")\n                print(f\"Channels: {int(np.mean(image_stats['channels']))}\")\n                print(f\"Average file size: {np.mean(image_stats['file_sizes']):.2f} KB\")\n                print(f\"File size range: {np.min(image_stats['file_sizes']):.2f} - {np.max(image_stats['file_sizes']):.2f} KB\")\n                print(f\"Mean brightness: {np.mean(image_stats['mean_brightness']):.2f} ± {np.std(image_stats['mean_brightness']):.2f}\")\n        \n        return image_stats\n\n    image_stats = analyze_sample_images(sample_size=200)\n    \n    # Visualize brightness distribution if we have data\n    if image_stats['mean_brightness']:\n        plt.figure(figsize=(12, 5))\n        \n        plt.subplot(1, 2, 1)\n        plt.hist(image_stats['mean_brightness'], bins=30, color='orange', alpha=0.7, edgecolor='black')\n        plt.title('Distribution of Image Brightness', fontsize=14, fontweight='bold')\n        plt.xlabel('Mean Brightness Value')\n        plt.ylabel('Frequency')\n        plt.grid(True, alpha=0.3)\n        \n        plt.subplot(1, 2, 2)\n        plt.scatter(image_stats['mean_brightness'], image_stats['std_brightness'], \n                    alpha=0.6, color='purple', s=30)\n        plt.title('Brightness Mean vs Standard Deviation', fontsize=14, fontweight='bold')\n        plt.xlabel('Mean Brightness')\n        plt.ylabel('Brightness Std Dev')\n        plt.grid(True, alpha=0.3)\n        \n        plt.tight_layout()\n        plt.show()\nelse:\n    print(\"Image directory not found or empty. Skipping image analysis.\")\n    print(\"This section would analyze image properties including dimensions, brightness, and quality.\")\n\n# =============================================================================\n# Section 5: Visual Sample Display (if images are available)\n# =============================================================================\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"SECTION 5: VISUAL SAMPLE DISPLAY\")\nprint(\"=\" * 80)\n\nif IMAGE_DIR and os.path.exists(IMAGE_DIR) and image_files:\n    def display_sherd_pairs(n_pairs=5):\n        \"\"\"Display exterior and interior views of pottery sherds\"\"\"\n        \n        # Find sherds with both exterior and interior images\n        sherd_groups = metadata_df.groupby('sherd_id')\n        sherds_with_both = []\n        \n        for sherd_id, group in sherd_groups:\n            sides = set(group['image_side'].values)\n            if 'exterior' in sides and 'interior' in sides:\n                # Check if images actually exist\n                exterior_exists = False\n                interior_exists = False\n                \n                for _, row in group.iterrows():\n                    image_path = os.path.join(IMAGE_DIR, f\"{row['image_id']}.jpg\")\n                    if os.path.exists(image_path):\n                        if row['image_side'] == 'exterior':\n                            exterior_exists = True\n                        else:\n                            interior_exists = True\n                \n                if exterior_exists and interior_exists:\n                    sherds_with_both.append(sherd_id)\n        \n        print(f\"Found {len(sherds_with_both):,} sherds with both exterior and interior images\")\n        \n        if sherds_with_both:\n            # Sample and display\n            sample_sherds = np.random.choice(sherds_with_both, \n                                           size=min(n_pairs, len(sherds_with_both)), \n                                           replace=False)\n            \n            fig, axes = plt.subplots(n_pairs, 2, figsize=(10, 5*n_pairs))\n            if n_pairs == 1:\n                axes = axes.reshape(1, -1)\n            \n            for idx, sherd_id in enumerate(sample_sherds):\n                sherd_data = metadata_df[metadata_df['sherd_id'] == sherd_id]\n                \n                for side_idx, side in enumerate(['exterior', 'interior']):\n                    side_data = sherd_data[sherd_data['image_side'] == side].iloc[0]\n                    image_path = os.path.join(IMAGE_DIR, f\"{side_data['image_id']}.jpg\")\n                    \n                    if os.path.exists(image_path):\n                        img = cv2.imread(image_path)\n                        if img is not None:\n                            img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n                            \n                            axes[idx, side_idx].imshow(img_rgb)\n                            axes[idx, side_idx].set_title(\n                                f\"{sherd_id} - {side.capitalize()}\\n\"\n                                f\"Type: {side_data['type']}, Part: {side_data['part']}, Unit: {side_data['unit']}\",\n                                fontsize=10\n                            )\n                            axes[idx, side_idx].axis('off')\n            \n            plt.suptitle('Sample Pottery Sherds - Exterior and Interior Views', \n                         fontsize=16, fontweight='bold', y=1.02)\n            plt.tight_layout()\n            plt.show()\n    \n    try:\n        display_sherd_pairs(n_pairs=3)\n    except Exception as e:\n        print(f\"Could not display images: {e}\")\nelse:\n    print(\"Image directory not found or empty. Skipping visual display.\")\n    print(\"This section would show exterior and interior views of pottery sherds.\")\n\n# =============================================================================\n# Section 6: Data Quality Assessment\n# =============================================================================\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"SECTION 6: DATA QUALITY ASSESSMENT\")\nprint(\"=\" * 80)\n\n# Check for missing images\nif IMAGE_DIR and os.path.exists(IMAGE_DIR):\n    print(\"Checking for missing images...\")\n    missing_images = []\n    sample_size = min(1000, len(metadata_df))  # Check a sample to avoid long runtime\n    sample_df = metadata_df.sample(n=sample_size, random_state=42)\n    \n    for _, row in sample_df.iterrows():\n        image_path = os.path.join(IMAGE_DIR, f\"{row['image_id']}.jpg\")\n        if not os.path.exists(image_path):\n            missing_images.append(row['image_id'])\n    \n    missing_rate = len(missing_images) / sample_size\n    estimated_missing = int(missing_rate * len(metadata_df))\n    print(f\"Missing images in sample: {len(missing_images)} out of {sample_size} ({missing_rate*100:.2f}%)\")\n    print(f\"Estimated total missing: ~{estimated_missing:,} images\")\nelse:\n    print(\"Image directory not found. Cannot check for missing images.\")\n\n# Analyze metadata completeness\nprint(\"\\nMetadata Completeness:\")\nfor col in metadata_df.columns:\n    null_pct = metadata_df[col].isnull().sum() / len(metadata_df) * 100\n    print(f\"  {col}: {100 - null_pct:.2f}% complete\")\n\n# =============================================================================\n# Section 7: Statistical Summary\n# =============================================================================\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"SECTION 7: STATISTICAL SUMMARY\")\nprint(\"=\" * 80)\n\n# Create summary statistics\nsummary_stats = {\n    'Total Records': len(metadata_df),\n    'Unique Sherds': metadata_df['sherd_id'].nunique(),\n    'Excavation Units': metadata_df['unit'].nunique(),\n    'Pottery Types': metadata_df['type'].nunique(),\n    'Pottery Parts': metadata_df['part'].nunique(),\n    'Images per Sherd': len(metadata_df) / metadata_df['sherd_id'].nunique()\n}\n\nprint(\"\\nDataset Summary:\")\nprint(\"-\" * 40)\nfor key, value in summary_stats.items():\n    if isinstance(value, float):\n        print(f\"{key}: {value:.2f}\")\n    else:\n        print(f\"{key}: {value:,}\")\n\n# Most common pottery types\nprint(\"\\nTop 10 Most Common Pottery Types:\")\nprint(metadata_df['type'].value_counts().head(10))\n\n# =============================================================================\n# Section 8: Key Insights and Recommendations\n# =============================================================================\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"SECTION 8: KEY INSIGHTS AND RECOMMENDATIONS\")\nprint(\"=\" * 80)\n\nprint(\"\"\"\nKey Findings from Exploratory Data Analysis:\n\n1. Dataset Scale and Structure:\n   - The dataset contains metadata for pottery fragments from the Daxinzhuang site\n   - Each fragment typically has both exterior and interior photographs\n   - Structured metadata includes stratigraphic context and pottery classification\n\n2. Archaeological Context:\n   - Multiple stratigraphic layers provide temporal context for the fragments\n   - Diverse pottery types and vessel parts are represented in the dataset\n   - The stratification data can be used as constraints in matching algorithms\n\n3. Data Quality Considerations:\n   - Check for missing images before processing\n   - Metadata appears to be complete with minimal null values\n   - Standardized naming conventions facilitate systematic processing\n\n4. Modeling Recommendations:\n   \n   a) Preprocessing Pipeline:\n      - Validate image availability before processing\n      - Implement robust error handling for missing or corrupted images\n      - Consider creating image pairs (exterior/interior) for each sherd\n   \n   b) Feature Extraction Approaches:\n      - Edge detection for fracture boundary identification\n      - Color and texture analysis for pottery type classification\n      - Shape descriptors for vessel part recognition\n   \n   c) Matching Strategy:\n      - Use stratigraphic information to constrain possible matches\n      - Leverage pottery type and part metadata to reduce search space\n      - Implement hierarchical matching: type → part → individual fragments\n   \n   d) Validation Approach:\n      - Create held-out test sets stratified by excavation unit\n      - Ensure evaluation metrics account for archaeological constraints\n      - Consider both pairwise accuracy and reconstruction completeness\n\n5. Implementation Workflow:\n   - Start with metadata-based filtering to create candidate pairs\n   - Apply visual feature matching within constrained sets\n   - Use graph-based optimization for global consistency\n   - Validate results against archaeological knowledge\n\nThis analysis provides a foundation for developing sophisticated matching algorithms\nthat combine computer vision techniques with archaeological domain knowledge.\n\"\"\")\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"END OF EXPLORATORY DATA ANALYSIS\")\nprint(\"=\" * 80)\nprint(\"\\nNext steps:\")\nprint(\"1. Implement image preprocessing pipeline\")\nprint(\"2. Develop feature extraction methods\")\nprint(\"3. Create matching algorithm with archaeological constraints\")\nprint(\"4. Build evaluation framework\")\nprint(\"\\nGood luck with the competition!\")","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}