Spaces:
Sleeping
Sleeping
| import pandas as pd | |
| import re | |
| import numpy as np | |
| # ============================================================================== | |
| # STAGE 1: DATA LOADING & MERGING | |
| # ============================================================================== | |
| # Veri setleri okunuyor (Dosyaların çalışma dizininde olduğu varsayılmıştır) | |
| try: | |
| df1 = pd.read_json("first400.json") | |
| df2 = pd.read_json("222veri.json") | |
| print("Veri setleri başarıyla yüklendi.") | |
| except FileNotFoundError: | |
| print("Hata: JSON dosyaları bulunamadı. Lütfen dosya yollarını kontrol edin.") | |
| # Kaynak takibi için etiketleme ve birleştirme | |
| df1['source_file'], df2['source_file'] = 'V1', 'V2' | |
| df_master = pd.concat([df1, df2], ignore_index=True) | |
| # Tüm sütun isimlerini liste halinde gör | |
| print("--- Master Dataset Sütunları ---") | |
| print(list(df_master.columns)) | |
| # Toplam kaç sütun olduğunu da yazdırma | |
| print(f"\nToplam Sütun Sayısı: {len(df_master.columns)}") | |
| # ============================================================================== | |
| # STAGE 2: COLUMN PRUNING (CLEANING) | |
| # ============================================================================== | |
| # Uygulama için katma değeri olmayan teknik ve reklam odaklı sütunların temizlenmesi | |
| redundant_columns = [ | |
| 'claimThisBusiness', 'googleFoodUrl', 'hotelAds', 'gasPrices', | |
| 'searchPageUrl', 'isAdvertisement', 'phoneUnformatted', | |
| 'additionalOpeningHours', 'updatesFromCustomers', 'checkInDate', | |
| 'checkOutDate', 'hotelStars', 'hotelDescription', 'reviewsDistribution', | |
| 'scrapedAt', 'locatedIn', 'language', 'countryCode', 'price', | |
| 'peopleAlsoSearch', 'placesTags', 'reviewsTags', 'imageCategories', 'reserveTableUrl' | |
| ] | |
| # Mevcut olan sütunları filtrele ve sil | |
| df_master = df_master.drop(columns=[col for col in redundant_columns if col in df_master.columns]) | |
| print(f"Gereksiz sütunlar temizlendi. Kalan sütun: {df_master.shape[1]}") | |
| print(f"Temizlik sonrası kalan sütun sayısı: {df_master.shape[1]}") | |
| # ============================================================================== | |
| # STAGE 3: CATEGORICAL & KEYWORD FILTERING (NOISE REDUCTION) | |
| # ============================================================================== | |
| # Ticari, tıbbi ve gezi rehberi dışı kategorilerin elenmesi | |
| excluded_categories = [ | |
| 'Travel agency', 'Tour agency', 'Sightseeing tour agency', 'Tour operator', | |
| 'Tourist information center', 'Car rental agency', 'Limousine service', | |
| 'Parking lot', 'Clinic', 'Medical center', 'Corporate office', | |
| 'Lodging', 'Hotel', 'Real estate agency' | |
| ] | |
| # Başlıkta (Title) geçtiğinde kaydı geçersiz kılan anahtar kelimeler | |
| excluded_keywords = [ | |
| 'Turizm', 'Travel', 'Agency', 'Acenta', 'Clinic', 'Klinik', | |
| 'Rent a Car', 'Transfer', 'Rota', 'Medical', 'Hizmetleri', | |
| 'Dog', 'Tour Poınt', 'Toplanma Salonu', 'Silivrikapı Geçiş', 'Mother and baby' | |
| ] | |
| # Filtreleme operasyonu | |
| df_master = df_master[~df_master['categoryName'].isin(excluded_categories)] | |
| pattern = '|'.join(excluded_keywords) | |
| df_master = df_master[~df_master['title'].str.contains(pattern, case=False, na=False)] | |
| print(f"Ticari ve ilgisiz yerler elendi. Güncel mekan sayısı: {len(df_master)}") | |
| # ============================================================================== | |
| # STAGE 4: GEO-NORMALIZATION (CITY MAPPING) | |
| # ============================================================================== | |
| def final_city_cleaner(text): | |
| """Karmaşık konum verilerini standart ilçe isimlerine map eder.""" | |
| if pd.isna(text): return "Diğer" | |
| text = str(text).lower().strip() | |
| # Manuel düzeltme gerektiren spesifik lokasyonlar | |
| special_mapping = { | |
| "haskoy": "Beyoğlu", "bulbol area": "Beyoğlu", "galataport": "Beyoğlu", | |
| "molla güarani": "Fatih", "sultanahmet/fatih": "Fatih", | |
| "vezirhan/fatih/fatih": "Fatih", "eyüp/eyüpsultan": "Eyüpsultan" | |
| } | |
| if text in special_mapping: return special_mapping[text] | |
| # İlçe anahtar kelime taraması | |
| districts = ["fatih", "beyoğlu", "beyoglu", "kadıköy", "kadikoy", "beşiktaş", | |
| "besiktas", "üsküdar", "uskudar", "şişli", "sisli", "eyüpsultan", | |
| "eyüp", "zeytinburnu"] | |
| for d in districts: | |
| if d in text: | |
| return d.replace('beyoglu', 'beyoğlu').replace('besiktas', 'beşiktaş').title() | |
| return text.title() | |
| df_master['city'] = df_master['city'].apply(final_city_cleaner) | |
| # Sonuçları kontrol et | |
| print("--- Temizlenmiş Şehir Dağılımı ---") | |
| print(df_master['city'].value_counts()) | |
| # ============================================================================== | |
| # STAGE 5: DATA IMPUTATION (NULL FILLING) | |
| # ============================================================================== | |
| # Puan ve yorum sayısı sütunlarındaki boşlukları 0 ile doldur | |
| df_master['totalScore'] = df_master['totalScore'].fillna(0) | |
| df_master['reviewsCount'] = df_master['reviewsCount'].fillna(0) | |
| # Telefon ve web sitesi sütunlarını standart yer tutucularla doldur | |
| df_master['phone'] = df_master['phone'].fillna("No contact information") | |
| df_master['website'] = df_master['website'].fillna("Website not available") | |
| # Eğer street hala null ise neighborhood ile doldur, o da yoksa boş bırak | |
| df_master['street'] = df_master['street'].fillna(df_master['neighborhood']).fillna("") | |
| # additionalInfo Null olanları boş liste ile doldurma | |
| df_master['additionalInfo'] = df_master['additionalInfo'].apply(lambda x: x if isinstance(x, (list, dict)) else []) | |
| # Değişikliği doğrula | |
| print(f"Puan ve yorum sayısı boş olan satırlar 0 ile dolduruldu.") | |
| print(f"Kalan boş totalScore sayısı: {df_master['totalScore'].isna().sum()}") | |
| print("'phone' ve 'website' sütunlarındaki boşluklar yer tutucu metinlerle dolduruldu.") | |
| print("Street sütunu için fallback (neighborhood) uygulandı.") | |
| # ============================================================================== | |
| # STAGE 6: FEATURE ENGINEERING (BAYESIAN RATING) | |
| # ============================================================================== | |
| def calculate_weighted_rating(df): | |
| """Düşük yorum sayılı yüksek puanlı mekanları dengelemek için Bayesian Average kullanır.""" | |
| C = df['totalScore'].mean() # Global ortalama puan | |
| m = df['reviewsCount'].quantile(0.25) # Minimum güvenilirlik eşiği (Q1) | |
| return df.apply(lambda row: (row['totalScore'] * row['reviewsCount'] + C * m) / | |
| (row['reviewsCount'] + m), axis=1) | |
| # Eksik puanları 0 ile normalize etme ve ağırlıklı puanı hesaplama | |
| df_master[['totalScore', 'reviewsCount']] = df_master[['totalScore', 'reviewsCount']].fillna(0) | |
| df_master['weighted_score'] = calculate_weighted_rating(df_master) | |
| print("Bayesian Ağırlıklı Puanlama (weighted_score) hesaplandı.") | |
| # ============================================================================== | |
| # STAGE 7: DYNAMIC FEATURE EXTRACTION (ONE-HOT ENCODING) | |
| # ============================================================================== | |
| def discover_all_features(df): | |
| """additionalInfo içindeki tüm hiyerarşik özellikleri keşfeder.""" | |
| unique_features = set() | |
| for info in df['additionalInfo']: | |
| if isinstance(info, dict): | |
| for category, items in info.items(): | |
| if isinstance(items, list): | |
| for item in items: | |
| for feature_name in item.keys(): | |
| unique_features.add(f"{category}_{feature_name}") | |
| return list(unique_features) | |
| # Keşfedilen her özellik için dinamik sütun oluşturma (True/False) | |
| all_possible_columns = discover_all_features(df_master) | |
| def dynamic_filler(row, feature_path): | |
| category, feature_name = feature_path.split('_', 1) | |
| info = row.get('additionalInfo', {}) | |
| if not isinstance(info, dict): return False | |
| category_list = info.get(category, []) | |
| if isinstance(category_list, list): | |
| for item in category_list: | |
| if item.get(feature_name) is True: return True | |
| return False | |
| for feature in all_possible_columns: | |
| clean_col_name = feature.replace(" ", "_").replace(":", "").lower() | |
| df_master[clean_col_name] = df_master.apply(lambda r: dynamic_filler(r, feature), axis=1) | |
| print(f"Dinamik Özellik Çıkarımı Tamamlandı. {len(all_possible_columns)} yeni özellik eklendi.") | |
| print("Veri işleme süreci başarıyla sonuçlandı.") | |
| # ============================================================================== | |
| # STAGE 8: TEMPORAL ANALYSIS & OPENING HOURS IMPUTATION | |
| # ============================================================================== | |
| def check_24_7(hours_list): | |
| """Mekanın 7/24 açık olup olmadığını kontrol eder.""" | |
| if not isinstance(hours_list, list) or len(hours_list) == 0: | |
| return False | |
| return all("Open 24 hours" in str(day.get('hours', '')) for day in hours_list) | |
| def impute_opening_hours(row): | |
| """Eksik çalışma saatlerini kategori bazlı tahmini verilerle doldurur.""" | |
| hours = row.get('openingHours') | |
| if isinstance(hours, list) and len(hours) > 0: | |
| return hours | |
| cat = str(row.get('main_category', '')) | |
| # Kategori bazlı standart çalışma saatleri ataması | |
| mapping = { | |
| 'Religious & Spiritual': "Open according to prayer times (Generally 05:00 - 22:00)", | |
| 'Museum & Art': "Generally 09:00 - 17:00 (Check before visiting, may be closed on Mondays)", | |
| 'History & Heritage': "Generally 09:00 - 17:00 (Check before visiting, may be closed on Mondays)", | |
| 'Nature & Parks': "Open 24 hours every day of the week", | |
| 'Squares & Plazas': "Open 24 hours every day of the week", | |
| 'Shopping & Traditional Bazaar': "Generally 09:00 - 19:00 (May vary on Sundays)", | |
| 'Food & Drink': "Generally 08:00 - 22:00 (Varies by establishment)" | |
| } | |
| return mapping.get(cat, "Opening hours not specified") | |
| # 24/7 bayrağı ve UI için görüntüleme sütunları oluşturuluyor | |
| df_master['is_24_7'] = df_master['openingHours'].apply(check_24_7) | |
| df_master['openingHours_display'] = df_master.apply(impute_opening_hours, axis=1) | |
| df_master['is_hours_estimated'] = df_master['openingHours'].apply(lambda x: not (isinstance(x, list) and len(x) > 0)) | |
| # ============================================================================== | |
| # STAGE 9: LOCALIZATION & SMART TITLE SYNTHESIS | |
| # ============================================================================== | |
| def tr_title(text): | |
| """Türkçe karakter duyarlı Title Case dönüşümü yapar.""" | |
| if pd.isna(text): return "" | |
| words = str(text).split() | |
| fixed_words = [] | |
| for w in words: | |
| if w.startswith('i'): fixed_words.append('İ' + w[1:].lower()) | |
| elif w.startswith('ı'): fixed_words.append('I' + w[1:].lower()) | |
| else: fixed_words.append(w.capitalize()) | |
| return " ".join(fixed_words) | |
| def smart_title_fix(row): | |
| """TR ve ENG başlıkları akıllıca birleştirerek 'display_title' oluşturur.""" | |
| eng_title = str(row.get('title', '')).strip() | |
| tr_title_val = str(row.get('subTitle', '')).strip() | |
| if not tr_title_val or tr_title_val.lower() == eng_title.lower() or tr_title_val == "None": | |
| return eng_title | |
| return f"{tr_title_val} ({eng_title})" | |
| df_master['subTitle'] = df_master['subTitle'].apply(tr_title) | |
| df_master['display_title'] = df_master.apply(smart_title_fix, axis=1) | |
| # ============================================================================== | |
| # STAGE 10: SPATIAL DATA EXTRACTION (COORDINATES) | |
| # ============================================================================== | |
| # Karmaşık location sözlüğü latitude ve longitude sütunlarına ayrıştırılıyor | |
| coords = df_master['location'].apply(lambda x: pd.Series(x) if isinstance(x, dict) else pd.Series({'lat': np.nan, 'lng': np.nan})) | |
| df_master['latitude'], df_master['longitude'] = coords['lat'], coords['lng'] | |
| df_master = df_master.drop(columns=['location']) | |
| # ============================================================================== | |
| # STAGE 11: QUALITY SCORING & DEDUPLICATION | |
| # ============================================================================== | |
| def calculate_record_score(row): | |
| """Veri doluluğuna göre kaydın kalite puanını hesaplar (Max: 35).""" | |
| score = 0 | |
| if pd.notna(row.get('description')) and len(str(row['description'])) > 5: score += 10 | |
| if isinstance(row.get('additionalInfo'), (dict, list)) and len(row['additionalInfo']) > 0: score += 8 | |
| if isinstance(row.get('openingHours'), list) and len(row['openingHours']) > 0: score += 7 | |
| if pd.notna(row.get('subTitle')) and len(str(row['subTitle'])) > 2: score += 5 | |
| if pd.notna(row.get('reviewsCount')): score += 3 | |
| if pd.notna(row.get('totalScore')): score += 2 | |
| return score | |
| # Skorlama ve en kaliteli kaydı üstte tutacak şekilde sıralama | |
| df_master['quality_score'] = df_master.apply(calculate_record_score, axis=1) | |
| df_master = df_master.sort_values(by=['placeId', 'quality_score'], ascending=[True, False]) | |
| # Mükerrer kayıtların (Duplicate) temizlenmesi | |
| duplicate_count = df_master['placeId'].duplicated().sum() | |
| df_master = df_master.drop_duplicates(subset=['placeId'], keep='first') | |
| print(f"Tekilleştirme: {duplicate_count} mükerrer kayıt elendi.") | |
| print(f"Benzersiz mekan sayısı: {len(df_master)}") | |
| # ============================================================================== | |
| # STAGE 12: CHARACTER SET VALIDATION (FOREIGN LANGUAGE FILTER) | |
| # ============================================================================== | |
| # Latin ve Türkçe alfabesi dışındaki (Arapça, Kiril vb.) karakterleri yakalayan Regex | |
| non_latin_regex = r'[^\x00-\x7FİıĞğÜüŞşÖöÇçâîûÂÎÛ\s\d\.,\-\(\)\&\'\!]' | |
| foreign_data = df_master[df_master['title'].str.contains(non_latin_regex, na=False, regex=True)].copy() | |
| print(f"--- Toplam {len(foreign_data)} Adet Yabancı Alfabeli/Karakterli Veri Bulundu ---\n") | |
| if len(foreign_data) > 0: | |
| # Tüm listeyi görmek için kısıtlamayı kaldıralım | |
| pd.set_option('display.max_rows', None) | |
| print("--- Silinecek Yabancı Kayıtlar ---") | |
| # Listeyi ekrana yazdır (Örn: Arapça, Çince veya Kiril isimli mekanlar) | |
| print(foreign_data[['title', 'city', 'source_file']]) | |
| # Hangi dosyadan ne kadar yabancı veri geldiğini görelim | |
| print("\n--- Kaynak Dosya Dağılımı ---") | |
| print(foreign_data['source_file'].value_counts()) | |
| # 3. TEMİZLİK: Bu yabancı kayıtları ana veriden (df_master) silelim | |
| df_master = df_master.drop(foreign_data.index) | |
| print(f"\n✅ Yabancı alfabeli {len(foreign_data)} kayıt temizlendi.") | |
| pd.reset_option('display.max_rows') | |
| else: | |
| print("Latin dışı hiçbir karakter bulunamadı.") | |
| # ============================================================================== | |
| # STAGE 13: CATEGORY MAPPING & CLEANING | |
| # ============================================================================== | |
| # 1. Kara Liste Tanımlama (Gezi rehberinde asla olmaması gerekenler) | |
| kara_liste = [ | |
| 'Turizm', 'Tourism', 'Agency', 'Travel', 'Clinic', 'Coiffeur', 'Halı', 'Carpet', | |
| 'Workshop', 'Medical', 'Acente', 'Gayrimenkul', 'Emlak', 'Acentesi', 'Global', 'AGN TURİZM','otobüsleri', 'kalkış noktası', 'bus station', 'departure' | |
| ] | |
| # 2. Gelişmiş Mapping Kuralları (Sıralama Önceliği Korunarak Güncellendi) | |
| # NOT: 'Food & Drink' en sondadır. Böylece tarihi bir kafe önce 'History' olarak yakalanır. | |
| mapping_rules = { | |
| 'Food & Drink': [ | |
| 'restaurant', 'cafe', 'coffee', 'kebab', 'bakery', 'pub', 'bar', 'breakfast', 'lokanta', 'kahve', 'fırın', | |
| 'tatlıcı', 'pastane', 'meyhane', 'döner', 'pizza', 'steakhouse', 'grill', 'bistro', 'patisserie', 'brasserie', | |
| 'cafeteria', 'mutfağı', 'sofrası', 'kebap', 'köfte', 'dürüm', 'restorant', 'winery', 'gastronomi' | |
| ], | |
| 'Museum & Art': [ | |
| 'museum', 'art', 'gallery', 'exhibition', 'müze','müzesi', 'galeri', 'sergi', 'sanat', 'atolye', 'atölye', | |
| 'theater', 'opera', 'sinema', 'kütüphane', 'library', 'kültür merkezi', 'cultural center','mural', 'murral', 'murales', 'streetart', 'graffiti', 'stairs', 'staircase', 'merdiven' | |
| ], | |
| 'Religious & Spiritual': [ | |
| 'mosque', 'church', 'synagogue', 'tomb', 'hazire', 'cemetery', 'cami','camii' 'kilise', 'havra', | |
| 'türbe', 'hazire', 'mezarlık', 'dergah', 'tekke', 'namazgah', 'kabri', 'mezarı', 'kabristan', | |
| 'mausoleum', 'cathedral' | |
| ], | |
| 'History & Heritage': [ | |
| 'historical', 'monument', 'castle', 'palace', 'bridge', 'tower', 'landmark', 'saray', 'kale', 'kule', 'köprü', 'anıt', | |
| 'tarihi', 'sarnıç', 'cistern', 'hamam', 'bath', 'aqueduct', 'su kemeri', 'fountain', 'çeşme', 'sebil', 'terazi', | |
| 'obelisk', 'dikilitaş', 'köşk', 'pavilion', 'mansion', 'kasrı', 'sur', 'kapısı', 'gate', 'fortress', 'walls', | |
| 'medrese', 'madrasa', 'taşı', 'tekfur', 'bedesten','feneri', 'lighthouse', 'anıtı', 'memorial', 'statue', 'heykel', r'\bhanı\b' # \bhanı\b ile hancı kelimesini engelledik | |
| ], | |
| 'Shopping & Traditional Bazaar': ['bazaar', 'market', 'han', 'çarşı', 'pazar', 'shopping', 'mall', 'bedesten', 'arasta', 'pasaj'], | |
| 'Nature & Parks': [ | |
| 'park', 'garden', 'scenic', 'nature', 'forest', 'island', 'bahçe', 'koru', 'ada', 'doğa', 'manzara', | |
| 'hill', 'tepe', 'sahil', 'coast', 'plaj', 'yürüyüş yolu', 'köy yolu','sunset', 'günbatımı', 'spot', 'seyir' # 'yol' kelimesi adreslerle karışmaması için spesifikleştirildi | |
| ], | |
| 'Squares & Plazas': ['plaza', 'square', 'meydan', 'alanı', 'iskelesi', 'iskeleye', 'eminönü', 'beşiktaş'] | |
| } | |
| def evliyapp_mapper(row): | |
| title = str(row.get('title', '')).lower() | |
| sub_title = str(row.get('subTitle', '')).lower() | |
| cat_main = str(row.get('categoryName', '')).lower() | |
| # Categories listesini güvenli şekilde birleştir | |
| cats_list = row.get('categories', []) | |
| cats_joined = " ".join(cats_list).lower() if isinstance(cats_list, list) else "" | |
| # --- ADIM 1: Kara Liste Kontrolü (Sadece Başlık ve subTitle üzerinden) --- | |
| if any(word.lower() in title or word.lower() in sub_title for word in kara_liste): | |
| return "DELETE", "Blacklist" | |
| # --- ADIM 2: Süper Birleşik Metin (Combined) --- | |
| # Başlık + Türkçe İsim + Ana Kategori + Kategori Listesi | |
| combined = f"{title} {sub_title} {cat_main} {cats_joined}" | |
| # --- ADIM 3: Kategori Mapping --- | |
| for main_cat, keywords in mapping_rules.items(): | |
| for word in keywords: | |
| # Eğer kelime zaten regex (\b) içeriyorsa doğrudan kullan, yoksa oluştur | |
| if r'\b' in word: | |
| pattern = word | |
| elif len(word) <= 4: | |
| pattern = r'\b' + re.escape(word) + r'\b' | |
| else: | |
| pattern = re.escape(word) | |
| if re.search(pattern, combined): | |
| return main_cat, word.title() | |
| return 'Sightseeing', 'General' | |
| results = df_master.apply(lambda r: pd.Series(evliyapp_mapper(r)), axis=1) | |
| df_master['main_category'] = results[0] | |
| df_master['sub_category'] = results[1] | |
| df_master = df_master[df_master['main_category'] != "DELETE"].copy() | |
| # ============================================================================== | |
| # STAGE 14: UPDATING THE DESCRIPTION COLUMN | |
| # ============================================================================== | |
| def generate_display_description(row): | |
| # 1. Mevcut ve yeterli bir açıklama varsa onu koru | |
| original = str(row.get('description', '')).strip() | |
| if pd.notna(row.get('description')) and len(original) > 25: | |
| return original | |
| # 2. Temel Değişkenler | |
| title = row.get('title', 'This location') | |
| city = row.get('city', 'Istanbul') | |
| main_cat = str(row.get('main_category', 'point of interest')).lower() | |
| score = row.get('totalScore') | |
| # 3. Kategoriye Özel Estetik Cümleler | |
| if main_cat == 'food & drink': | |
| sentence = f"Discover the local flavors at {title}, a popular spot in {city} known for its inviting atmosphere." | |
| elif main_cat == 'religious & spiritual': | |
| sentence = f"{title} stands as a serene spiritual site in {city}, offering visitors a peaceful retreat and historical depth." | |
| elif main_cat == 'museum & art': | |
| sentence = f"Immerse yourself in cultural heritage at {title}, where {city}'s artistic and historical legacy comes to life." | |
| elif main_cat == 'history & heritage': | |
| sentence = f"{title} is a landmark of historical significance in {city}, reflecting the rich architectural tapestry of the area." | |
| elif main_cat == 'shopping & traditional bazaar': | |
| sentence = f"Experience the authentic vibe of {city} at {title}, a vibrant destination perfect for traditional shopping and local crafts." | |
| elif main_cat == 'nature & parks': | |
| sentence = f"Enjoy a breath of fresh air at {title}, a beautiful green space in {city} ideal for relaxation and outdoor moments." | |
| elif main_cat == 'squares & plazzas': # Yazım hatası olasılığına karşı kontrol | |
| sentence = f"{title} is a central landmark in {city}, serving as a lively meeting point and a great spot to observe city life." | |
| else: | |
| sentence = f"{title} is a must-visit {main_cat} in {city}, contributing to the unique and diverse charm of the district." | |
| # 4. SOSYAL KANIT EKLEME (Social Proof) | |
| # Eğer mekanın puanı yüksekse açıklamaya bir 'güven' cümlesi ekler | |
| if pd.notna(score) and score >= 4.2: | |
| sentence += f" It is highly recommended by visitors with a remarkable {score} rating." | |
| return sentence | |
| df_master['description'] = df_master.apply(generate_display_description, axis=1) | |
| # ============================================================================== | |
| # STAGE 15: CREATING A TAGS COLUMN | |
| # ============================================================================== | |
| # Eşleşme garantisi için anahtarları (keys) küçük harf yapılır | |
| raw_mapping = { | |
| # Accessibility | |
| "Accessibility_Wheelchair accessible entrance": "Wheelchair Accessible", | |
| "Accessibility_Wheelchair-accessible entrance": "Wheelchair Accessible", | |
| "Accessibility_Wheelchair accessible parking lot": "Wheelchair Parking", | |
| "Accessibility_Wheelchair-accessible toilet": "Wheelchair Accessible", | |
| "Accessibility_Wheelchair accessible restroom": "Wheelchair Accessible", | |
| # Atmosphere | |
| "Atmosphere_Cosy": "Cozy", | |
| "Atmosphere_Cozy": "Cozy", | |
| "Atmosphere_Trending": "Trendy", | |
| "Atmosphere_Trendy": "Trendy", | |
| "Atmosphere_Historic": "Historic", | |
| "Atmosphere_History": "Historic", | |
| # Amenities & Service | |
| "Amenities_Toilet": "Restroom", | |
| "Amenities_Public restroom": "Restroom", | |
| "Amenities_Wi-Fi": "Free Wi-Fi", | |
| "Service options_Takeaway": "Takeout", | |
| "Service options_Takeout": "Takeout", | |
| # Children | |
| "Children_Good for kids": "Child Friendly", | |
| "Children_Kid-friendly activities": "Child Friendly", | |
| "Children_Good for kids birthday": "Child Friendly", | |
| # Payments & Other | |
| "Payments_Admission fee": "Entry Fee Required", # İki girişin vardı, 'Required' olanı seçtim | |
| "Offerings_Halal food": "Halal Options", | |
| "Highlights_Rooftop seating": "Rooftop", | |
| "Parking_lot": "Parking Available", | |
| "Parking_Paid_parking_lot": "Paid Parking", | |
| "Payments_Credit_cards": "Credit Cards", | |
| "Pets_Dogs allowed inside": "Dog Friendly", | |
| "Pets_Dogs allowed outside": "Dog Friendly" | |
| } | |
| # Kodun içindeki karşılaştırma için mapping'i normalize etme | |
| tag_mapping = {k.lower().replace(" ", "_").replace(":", ""): v for k, v in raw_mapping.items()} | |
| # UI'da gizlenilecek ama veride tuttuğumuz liste | |
| noise_tags = ["Credit Cards", "Debit Cards", "Nfc Mobile Payments", | |
| "Dine-In", "Table Service", "Seating", "Groups", "Tourists", | |
| "Lunch", "Dinner", "Brunch",'Food', 'Service', 'Dining'] | |
| def generate_all_tags(row, feature_columns, rules): | |
| tags = set() | |
| # --- BÖLÜM A: KATEGORİ MAPPING (Kelimelerden kategori yakalama) --- | |
| title = str(row.get('title', '')).lower() | |
| sub_title = str(row.get('subTitle', '')).lower() | |
| cat_name = str(row.get('categoryName', '')).lower() | |
| cats_list = row.get('categories', []) | |
| cats_joined = " ".join(cats_list).lower() if isinstance(cats_list, list) else "" | |
| combined_text = f"{title} {sub_title} {cat_name} {cats_joined}" | |
| for main_cat, keywords in rules.items(): | |
| for word in keywords: | |
| pattern = r'\b' + re.escape(word) + r'\b' if len(word) <= 4 else re.escape(word) | |
| if re.search(pattern, combined_text): | |
| tags.add(main_cat) | |
| break | |
| # --- BÖLÜM B: OPTİMİZE EDİLMİŞ DİNAMİK ÖZELLİKLER (144 Sütun İşleme) --- | |
| for col in feature_columns: | |
| if row.get(col) == True: | |
| # 1. ADIM: Önce sözlükte (tag_mapping) özel bir karşılığı var mı? | |
| # Sütun isimleri küçük harf olduğu için tag_mapping de küçük harf bakıyor | |
| if col in tag_mapping: | |
| clean_tag = tag_mapping[col] | |
| else: | |
| # 2. ADIM: Sözlükte yoksa, dinamik temizlik yap. | |
| if "_" in col: | |
| clean_tag = col.split("_", 1)[1] | |
| else: | |
| clean_tag = col | |
| # Alt tireleri boşluğa çevir ve Baş Harflerini Büyüt | |
| clean_tag = clean_tag.replace("_", " ").title().strip() | |
| # Özel düzeltmeler (lot -> Parking Lot gibi) | |
| if clean_tag == "Lot": clean_tag = "Parking Lot" | |
| if clean_tag == "Cards": clean_tag = "Credit Cards" | |
| # 3. ADIM: Son kontrol (Anlamsız çok kısa tagleri engelle) | |
| if len(clean_tag) > 2: | |
| tags.add(clean_tag) | |
| return list(tags) | |
| # --- UYGULAMA --- | |
| # Dinamik sütun listesini hazırla - BURAYA .lower() EKLENDİ (Kritik Düzeltme) | |
| dynamic_cols = [c.replace(" ", "_").replace(":", "").lower() for c in all_possible_columns] | |
| existing_dynamic_cols = [col for col in dynamic_cols if col in df_master.columns] | |
| print(f"Eşleşen dinamik sütun sayısı: {len(existing_dynamic_cols)}") | |
| # Tags sütununu oluştur | |
| df_master['tags'] = df_master.apply(lambda r: generate_all_tags(r, existing_dynamic_cols, mapping_rules), axis=1) | |
| # Sadece tags listesi boş olan ve kategorisi 'General' kalan 'noise' mekanları temizle | |
| df_master = df_master[~((df_master['tags'].map(len) == 0) & (df_master['main_category'] == 'Sightseeing (General)'))] | |
| print("'tags' sütunu başarıyla zenginleştirildi ve temizlik yapıldı.") | |
| # ============================================================================== | |
| # STAGE 16: ENRICHED DESCRIPTION FOR PGVECTOR | |
| # ============================================================================== | |
| def synthesize_enriched_description(row): | |
| # 1. Temel Kimlik | |
| title = str(row.get('display_title', row.get('title', ''))) | |
| main_cat = str(row.get('main_category', '')) | |
| sub_cat = str(row.get('sub_category', '')) | |
| # 2. Etiketler | |
| tags_list = row.get('tags', []) | |
| tags_text = ", ".join(tags_list) if isinstance(tags_list, list) else "" | |
| # 3. Saat Bilgisi | |
| hours = row.get('openingHours_display', '') | |
| if isinstance(hours, list): | |
| hours = "Open 24 hours" if row.get('is_24_7') else "Check hours online" | |
| score = str(row.get('totalScore', '')) | |
| original_desc = str(row.get('description', '')) | |
| # 4. SENTEZ | |
| enriched = f"{title}. Category: {main_cat} ({sub_cat}). " | |
| if tags_text: | |
| enriched += f"Features: {tags_text}. " | |
| if hours: | |
| enriched += f"Status: {hours}. " | |
| enriched += f"Info: {original_desc} " | |
| if score and score != '0': | |
| enriched += f"Rating: {score}/5." | |
| return enriched | |
| df_master['enriched_description'] = df_master.apply(synthesize_enriched_description, axis=1) | |
| print("enriched_description sütunu dinamik özelliklerle birlikte oluşturuldu.") | |
| # ============================================================================== | |
| # STAGE 17: Refined Version | |
| # ============================================================================== | |
| final_cols = [col for col in df_master.columns if col not in existing_dynamic_cols] | |
| df_final = df_master[final_cols] | |
| print(f"Eski sütun sayısı: {len(df_master.columns)}") | |
| print(f"Yeni sütun sayısı: {len(df_final.columns)}") | |
| print(f"Kalan Sütunlar: {df_final.columns.tolist()}") | |
| # AYRIŞTIRMA (Refined Version) | |
| df_food_app = df_master[df_master['main_category'] == 'Food & Drink'].copy().reset_index(drop=True) | |
| # Geri kalan her şeyi Sightseeing'e atıyoruz ama Food & Drink olmayanları filtreleyerek | |
| df_sightseeing_app = df_master[df_master['main_category'] != 'Food & Drink'].copy().reset_index(drop=True) | |
| # EKSTRA KONTROL: Kategori Atanamamış Kayıtlar | |
| # Eğer main_category null kaldıysa bu kayıtlar 'Genel' bile olamamış demektir | |
| null_cats = df_master[df_master['main_category'].isna()] | |
| # RAPORLAMA VE HATA AYIKLAMA (DEBUGGING) | |
| print(f"--- FİNAL OPERASYON RAPORU ---") | |
| print(f"Toplam İşlenen Kayıt: {len(df_master)}") | |
| print(f"Yemek Yerleri: {len(df_food_app)}") | |
| print(f"Gezi Rehberi: {len(df_sightseeing_app)}") | |
| print(f"Kategori Atanamayan (Null): {len(null_cats)}") | |
| print(f"\n--- Gezi Rehberi Alt Kategori Detayları ---") | |
| print(df_sightseeing_app['main_category'].value_counts()) | |
| # 8. SIGHTSEEING (GENEL) ANALİZİ | |
| genel_sayisi = len(df_sightseeing_app[df_sightseeing_app['main_category'] == 'Sightseeing (Genel)']) | |
| if genel_sayisi > 0: | |
| print(f"\n'Sightseeing (Genel)' grubunda {genel_sayisi} mekan var.") | |
| print("En sık rastlanan kategoriName örnekleri:") | |
| print(df_sightseeing_app[df_sightseeing_app['main_category'] == 'Sightseeing (Genel)']['categoryName'].value_counts().head(10)) | |
| # DOSYALARI KAYDET | |
| df_food_app.to_json('data_food_ready.json', orient='records', force_ascii=False) | |
| df_sightseeing_app.to_json('data_sightseeing_ready.json', orient='records', force_ascii=False) | |
| print("\nDosyalar 'ready' formatında kaydedildi.") | |