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.")