Spaces:
Running
Running
Commit
·
aebf0a2
1
Parent(s):
db7fce3
progress more 71
Browse files
app.py
CHANGED
@@ -80,7 +80,56 @@ rubert1 = pipeline("sentiment-analysis", model = "DeepPavlov/rubert-base-cased")
|
|
80 |
rubert2 = pipeline("sentiment-analysis", model = "blanchefort/rubert-base-cased-sentiment")
|
81 |
|
82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
@st.cache_resource
|
86 |
def load_model(model_id):
|
@@ -343,78 +392,66 @@ def process_file(uploaded_file):
|
|
343 |
|
344 |
st.write(f"Из {original_news_count} новостных сообщений удалены {duplicates_removed} дублирующих. Осталось {remaining_news_count}.")
|
345 |
|
346 |
-
st.write("Начинаем предобработку и анализ текстов...")
|
347 |
-
|
348 |
-
texts = df['Выдержки из текста'].tolist()
|
349 |
-
texts = [str(text) if not pd.isna(text) else "" for text in texts]
|
350 |
-
|
351 |
-
lemmatized_texts = [lemmatize_text(text) for text in texts]
|
352 |
-
translated_texts = batch_translate(lemmatized_texts)
|
353 |
-
df['Translated'] = translated_texts
|
354 |
-
|
355 |
-
# Perform sentiment analysis
|
356 |
-
df['ruBERT2'] = [get_rubert2_sentiment(text) for text in texts]
|
357 |
-
df['FinBERT'] = [get_finbert_sentiment(text) for text in translated_texts]
|
358 |
-
df['RoBERTa'] = [get_roberta_sentiment(text) for text in translated_texts]
|
359 |
-
df['FinBERT-Tone'] = [get_finbert_tone_sentiment(text) for text in translated_texts]
|
360 |
-
|
361 |
# Initialize LLM
|
362 |
llm = init_langchain_llm()
|
363 |
if not llm:
|
364 |
st.error("Не удалось инициализировать нейросеть. Пожалуйста, проверьте настройки и попробуйте снова.")
|
365 |
st.stop()
|
366 |
|
367 |
-
#
|
368 |
-
df['
|
369 |
-
df['
|
|
|
370 |
|
371 |
progress_bar = st.progress(0)
|
372 |
status_text = st.empty()
|
373 |
|
|
|
374 |
for index, row in df.iterrows():
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
progress = (index + 1) / len(df)
|
387 |
progress_bar.progress(progress)
|
388 |
status_text.text(f"Проанализировано {index + 1} из {len(df)} новостей")
|
389 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
390 |
progress_bar.empty()
|
391 |
status_text.empty()
|
392 |
|
|
|
393 |
visualization = generate_sentiment_visualization(df)
|
394 |
if visualization:
|
395 |
st.pyplot(visualization)
|
396 |
|
397 |
return df
|
398 |
|
399 |
-
def create_output_file(df, uploaded_file
|
400 |
-
# Load the sample file to use as a template
|
401 |
wb = load_workbook("sample_file.xlsx")
|
402 |
|
403 |
-
#
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
(entity_df['FinBERT-Tone'] == 'Negative'))
|
412 |
-
positive_news = sum((entity_df['FinBERT'] == 'Positive') |
|
413 |
-
(entity_df['RoBERTa'] == 'Positive') |
|
414 |
-
(entity_df['FinBERT-Tone'] == 'Positive'))
|
415 |
-
summary_data.append([entity, total_news, negative_news, positive_news])
|
416 |
-
|
417 |
-
summary_df = pd.DataFrame(summary_data, columns=['Объект', 'Всего новостей', 'Отрицательные', 'Положительные'])
|
418 |
summary_df = summary_df.sort_values('Отрицательные', ascending=False)
|
419 |
|
420 |
# Write 'Сводка' sheet
|
@@ -467,7 +504,7 @@ def create_output_file(df, uploaded_file, analysis_df):
|
|
467 |
|
468 |
def generate_sentiment_visualization(df):
|
469 |
# Filter for negative sentiments
|
470 |
-
negative_df = df[df[
|
471 |
|
472 |
if negative_df.empty:
|
473 |
st.warning("Не обнаружено негативных упоминаний. Отображаем общую статистику по объектам.")
|
@@ -479,38 +516,17 @@ def generate_sentiment_visualization(df):
|
|
479 |
st.warning("Нет данных для визуализации.")
|
480 |
return None
|
481 |
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
plt.tight_layout()
|
489 |
-
return fig
|
490 |
-
|
491 |
-
if len(entity_counts) <= 5:
|
492 |
-
st.info("Обнаружено малое количество объектов. Отображаем статистику в виде столбчатой диаграммы.")
|
493 |
-
fig, ax = plt.subplots(figsize=(10, 5))
|
494 |
-
entity_counts.plot(kind='bar', ax=ax)
|
495 |
-
ax.set_title('Количество упоминаний объектов')
|
496 |
-
ax.set_ylabel('Количество упоминаний')
|
497 |
-
plt.xticks(rotation=45, ha='right')
|
498 |
-
plt.tight_layout()
|
499 |
-
return fig
|
500 |
-
|
501 |
-
# If we have enough data, create a word cloud
|
502 |
-
wordcloud = WordCloud(width=800, height=400, background_color='white').generate_from_frequencies(entity_counts)
|
503 |
-
|
504 |
-
fig, ax = plt.subplots(figsize=(10, 5))
|
505 |
-
ax.imshow(wordcloud, interpolation='bilinear')
|
506 |
-
ax.axis('off')
|
507 |
-
ax.set_title('Облако слов: Объекты с негативными упоминаниями' if not negative_df.empty else 'Облако слов: Все упоминания объектов')
|
508 |
-
|
509 |
return fig
|
510 |
|
511 |
|
512 |
def main():
|
513 |
-
st.title("... приступим к анализу... версия
|
514 |
|
515 |
# Initialize session state
|
516 |
if 'processed_df' not in st.session_state:
|
|
|
80 |
rubert2 = pipeline("sentiment-analysis", model = "blanchefort/rubert-base-cased-sentiment")
|
81 |
|
82 |
|
83 |
+
def estimate_sentiment_and_impact(llm, news_text, entity):
|
84 |
+
template = """
|
85 |
+
Проанализируйте следующую новость об объекте "{entity}" и определите:
|
86 |
+
1. Тональность новости (Позитивная/Негативная/Нейтральная)
|
87 |
+
2. Оцените потенциальное финансовое влияние в рублях для этого объекта в ближайшие 6 месяцев.
|
88 |
+
|
89 |
+
Если точную денежную оценку дать невозможно, категоризируйте влияние как одно из следующих:
|
90 |
+
1. "Значительный риск убытков"
|
91 |
+
2. "Умеренный риск убытков"
|
92 |
+
3. "Незначительный риск убытков"
|
93 |
+
4. "Вероятность прибыли"
|
94 |
+
5. "Неопределенный эффект"
|
95 |
+
|
96 |
+
Также предоставьте краткое обоснование (максимум 100 слов).
|
97 |
|
98 |
+
Новость: {news}
|
99 |
+
|
100 |
+
Ответ дайте в следующем формате:
|
101 |
+
Sentiment: [Positive/Negative/Neutral]
|
102 |
+
Impact: [Ваша оценка или категория]
|
103 |
+
Reasoning: [Ваше обоснование]
|
104 |
+
"""
|
105 |
+
prompt = PromptTemplate(template=template, input_variables=["entity", "news"])
|
106 |
+
chain = prompt | llm | RunnablePassthrough()
|
107 |
+
response = chain.invoke({"entity": entity, "news": news_text})
|
108 |
+
|
109 |
+
# Parse the response
|
110 |
+
sentiment = "Neutral"
|
111 |
+
impact = "Неопределенный эффект"
|
112 |
+
reasoning = "Не удалось получить обоснование"
|
113 |
+
|
114 |
+
if isinstance(response, str):
|
115 |
+
try:
|
116 |
+
# Extract sentiment
|
117 |
+
if "Sentiment:" in response:
|
118 |
+
sentiment_part = response.split("Sentiment:")[1].split("\n")[0].strip().lower()
|
119 |
+
if "positive" in sentiment_part:
|
120 |
+
sentiment = "Positive"
|
121 |
+
elif "negative" in sentiment_part:
|
122 |
+
sentiment = "Negative"
|
123 |
+
|
124 |
+
# Extract impact and reasoning
|
125 |
+
if "Impact:" in response and "Reasoning:" in response:
|
126 |
+
impact_part, reasoning_part = response.split("Reasoning:")
|
127 |
+
impact = impact_part.split("Impact:")[1].strip()
|
128 |
+
reasoning = reasoning_part.strip()
|
129 |
+
except Exception as e:
|
130 |
+
st.error(f"Error parsing LLM response: {str(e)}")
|
131 |
+
|
132 |
+
return sentiment, impact, reasoning
|
133 |
|
134 |
@st.cache_resource
|
135 |
def load_model(model_id):
|
|
|
392 |
|
393 |
st.write(f"Из {original_news_count} новостных сообщений удалены {duplicates_removed} дублирующих. Осталось {remaining_news_count}.")
|
394 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
395 |
# Initialize LLM
|
396 |
llm = init_langchain_llm()
|
397 |
if not llm:
|
398 |
st.error("Не удалось инициализировать нейросеть. Пожалуйста, проверьте настройки и попробуйте снова.")
|
399 |
st.stop()
|
400 |
|
401 |
+
# Initialize columns for results
|
402 |
+
df['Sentiment'] = ''
|
403 |
+
df['Impact'] = ''
|
404 |
+
df['Reasoning'] = ''
|
405 |
|
406 |
progress_bar = st.progress(0)
|
407 |
status_text = st.empty()
|
408 |
|
409 |
+
# Process each news item
|
410 |
for index, row in df.iterrows():
|
411 |
+
sentiment, impact, reasoning = estimate_sentiment_and_impact(
|
412 |
+
llm,
|
413 |
+
row['Выдержки из текста'],
|
414 |
+
row['Объект']
|
415 |
+
)
|
416 |
+
|
417 |
+
df.at[index, 'Sentiment'] = sentiment
|
418 |
+
df.at[index, 'Impact'] = impact
|
419 |
+
df.at[index, 'Reasoning'] = reasoning
|
420 |
+
|
421 |
+
# Display progress
|
422 |
progress = (index + 1) / len(df)
|
423 |
progress_bar.progress(progress)
|
424 |
status_text.text(f"Проанализировано {index + 1} из {len(df)} новостей")
|
425 |
+
|
426 |
+
# Display each analysis result
|
427 |
+
st.write(f"Объект: {row['Объект']}")
|
428 |
+
st.write(f"Новость: {row['Заголовок']}")
|
429 |
+
st.write(f"Тональность: {sentiment}")
|
430 |
+
st.write(f"Эффект: {impact}")
|
431 |
+
st.write(f"Обоснование: {reasoning}")
|
432 |
+
st.write("---")
|
433 |
+
|
434 |
progress_bar.empty()
|
435 |
status_text.empty()
|
436 |
|
437 |
+
# Generate visualization after processing
|
438 |
visualization = generate_sentiment_visualization(df)
|
439 |
if visualization:
|
440 |
st.pyplot(visualization)
|
441 |
|
442 |
return df
|
443 |
|
444 |
+
def create_output_file(df, uploaded_file):
|
|
|
445 |
wb = load_workbook("sample_file.xlsx")
|
446 |
|
447 |
+
# Update summary sheet
|
448 |
+
summary_df = pd.DataFrame({
|
449 |
+
'Объект': df['Объект'].unique(),
|
450 |
+
'Всего новостей': df.groupby('Объект').size(),
|
451 |
+
'Негативные': df[df['Sentiment'] == 'Negative'].groupby('Объект').size(),
|
452 |
+
'Позитивные': df[df['Sentiment'] == 'Positive'].groupby('Объект').size(),
|
453 |
+
'Преобладающий эффект': df.groupby('Объект')['Impact'].agg(lambda x: x.value_counts().index[0])
|
454 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
455 |
summary_df = summary_df.sort_values('Отрицательные', ascending=False)
|
456 |
|
457 |
# Write 'Сводка' sheet
|
|
|
504 |
|
505 |
def generate_sentiment_visualization(df):
|
506 |
# Filter for negative sentiments
|
507 |
+
negative_df = df[df['Sentiment'] == 'Negative']
|
508 |
|
509 |
if negative_df.empty:
|
510 |
st.warning("Не обнаружено негативных упоминаний. Отображаем общую статистику по объектам.")
|
|
|
516 |
st.warning("Нет данных для визуализации.")
|
517 |
return None
|
518 |
|
519 |
+
# Create a horizontal bar chart showing entity risk levels
|
520 |
+
fig, ax = plt.subplots(figsize=(12, max(6, len(entity_counts) * 0.5)))
|
521 |
+
entity_counts.plot(kind='barh', ax=ax)
|
522 |
+
ax.set_title('Количество негативных упоминаний по объектам')
|
523 |
+
ax.set_xlabel('Количество упоминаний')
|
524 |
+
plt.tight_layout()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
525 |
return fig
|
526 |
|
527 |
|
528 |
def main():
|
529 |
+
st.title("... приступим к анализу... версия 71")
|
530 |
|
531 |
# Initialize session state
|
532 |
if 'processed_df' not in st.session_state:
|