ginipick commited on
Commit
5e373c3
ยท
verified ยท
1 Parent(s): c9f9775

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -146
app.py CHANGED
@@ -6,64 +6,10 @@ import requests
6
  from io import BytesIO
7
  import traceback
8
 
9
- def create_trend_chart(space_id, daily_ranks_df):
10
- if space_id is None or daily_ranks_df.empty:
11
- return None
12
-
13
- try:
14
- space_data = daily_ranks_df[daily_ranks_df['id'] == space_id].copy()
15
- if space_data.empty:
16
- return None
17
-
18
- space_data = space_data.sort_values('date')
19
-
20
- fig = px.line(
21
- space_data,
22
- x='date',
23
- y='rank',
24
- title=f'Daily Rank Trend for {space_id}',
25
- labels={'date': 'Date', 'rank': 'Rank'},
26
- markers=True,
27
- height=400
28
- )
29
-
30
- fig.update_layout(
31
- xaxis_title="Date",
32
- yaxis_title="Rank",
33
- yaxis=dict(
34
- range=[100, 1], # ๋žญํ‚น์ด 1์ด ์ตœ๊ณ ์ด๋ฏ€๋กœ y์ถ•์„ ๋’ค์ง‘์–ด์„œ ํ‘œ์‹œ
35
- tickmode='linear',
36
- tick0=1,
37
- dtick=10
38
- ),
39
- hovermode='x unified',
40
- plot_bgcolor='white',
41
- paper_bgcolor='white',
42
- showlegend=False,
43
- margin=dict(t=50, r=20, b=40, l=40)
44
- )
45
-
46
- fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='lightgray')
47
- fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='lightgray')
48
-
49
- fig.update_traces(
50
- line_color='#2563eb',
51
- line_width=2,
52
- marker=dict(size=8, color='#2563eb')
53
- )
54
-
55
- return fig
56
- except Exception as e:
57
- print(f"Error creating trend chart: {e}")
58
- traceback.print_exc()
59
- return None
60
-
61
  def load_and_process_data():
62
- """
63
- 1) spaces.parquet๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜์—ฌ DataFrame์œผ๋กœ ๋ณ€ํ™˜
64
- 2) ์ตœ๊ทผ 30์ผ์น˜ ๋ฐ์ดํ„ฐ๋งŒ ํ•„ํ„ฐ๋ง
65
- 3) ๋งค ์ผ์ž๋ณ„๋กœ trendingScore ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ rank๋ฅผ ๋งค๊ธด ๋’ค, ๋ˆ„์ 
66
- """
67
  try:
68
  url = "https://huggingface.co/datasets/cfahlgren1/hub-stats/resolve/main/spaces.parquet"
69
  response = requests.get(url)
@@ -74,7 +20,7 @@ def load_and_process_data():
74
  df['createdAt'] = pd.to_datetime(df['createdAt'])
75
  df = df[df['createdAt'] >= thirty_days_ago].copy()
76
 
77
- # 30์ผ ๋™์•ˆ์˜ ๋ชจ๋“  ๋‚ ์งœ์— ๋Œ€ํ•ด
78
  dates = pd.date_range(start=thirty_days_ago, end=datetime.now(), freq='D')
79
  daily_ranks = []
80
 
@@ -85,64 +31,58 @@ def load_and_process_data():
85
  date_data = date_data.sort_values(['trendingScore', 'id'], ascending=[False, True])
86
  date_data['rank'] = range(1, len(date_data) + 1)
87
  date_data['date'] = date.date()
88
-
89
  daily_ranks.append(
90
  date_data[['id', 'date', 'rank', 'trendingScore', 'createdAt']]
91
  )
92
 
93
- # ํ•˜๋ฃจํ•˜๋ฃจ ์Œ“์ธ rank ๊ธฐ๋ก์„ ๋ชจ๋‘ ํ•ฉ์นจ
94
  daily_ranks_df = pd.concat(daily_ranks, ignore_index=True)
95
 
96
- # ์ตœ์‹  ๋‚ ์งœ์˜ Top100๋งŒ ๋ณ„๋„๋กœ ์ถ”์ถœ
97
  latest_date = daily_ranks_df['date'].max()
98
- top_100_spaces = daily_ranks_df[
99
  (daily_ranks_df['date'] == latest_date) &
100
- (daily_ranks_df['rank'] <= 100)
101
  ].sort_values('rank').copy()
102
 
103
- return daily_ranks_df, top_100_spaces
104
  except Exception as e:
105
  print(f"Error loading data: {e}")
106
  traceback.print_exc()
107
  return pd.DataFrame(), pd.DataFrame()
108
 
109
- def get_top20_multiple_ids(daily_ranks_df):
 
 
 
110
  """
111
- '์ตœ์‹  ๋‚ ์งœ์˜ Top100'์—์„œ, ๋™์ผํ•œ id๊ฐ€ 2๊ฐœ ์ด์ƒ ๋“ฑ์žฅํ•˜๋Š” ๊ฒฝ์šฐ
112
- ํ•ด๋‹น ์Šค์ฝ”์–ด๋ฅผ ํ•ฉ์‚ฐํ•˜์—ฌ, ํ•ฉ๊ณ„๊ฐ€ ๋†’์€ ์ˆœ์œผ๋กœ top 20๊นŒ์ง€๋งŒ ๋ฆฌํ„ดํ•˜๋Š” ํ•จ์ˆ˜
 
113
  """
114
- if daily_ranks_df.empty:
115
  return pd.DataFrame()
116
 
117
  try:
118
- # ์ตœ์‹  ๋‚ ์งœ ํŒŒ์•…
119
- latest_date = daily_ranks_df['date'].max()
120
-
121
- # ์ตœ์‹  ๋‚ ์งœ์˜ Top100๋งŒ ํ•„ํ„ฐ๋ง
122
- latest_top100 = daily_ranks_df[
123
- (daily_ranks_df['date'] == latest_date) & (daily_ranks_df['rank'] <= 100)
124
- ].copy()
125
-
126
- # id๋ณ„ ๋“ฑ์žฅ ํšŸ์ˆ˜ (Top100 ๋‚ด)
127
- id_counts = latest_top100['id'].value_counts()
128
  # 2๊ฐœ ์ด์ƒ ๋“ฑ์žฅํ•˜๋Š” id๋งŒ ์ถ”์ถœ
129
  multiple_ids = id_counts[id_counts >= 2].index
130
 
131
  if len(multiple_ids) == 0:
132
- # ์ค‘๋ณต id๊ฐ€ ์•„์˜ˆ ์—†๋Š” ๊ฒฝ์šฐ
133
  return pd.DataFrame()
134
 
135
- # ์ค‘๋ณต๋œ id์— ํ•ด๋‹นํ•˜๋Š” ํ–‰๋งŒ ๋ชจ์œผ๊ธฐ
136
- multiple_entries = latest_top100[latest_top100['id'].isin(multiple_ids)].copy()
137
 
138
- # id๋ณ„๋กœ score ํ•ฉ์‚ฐ
139
  df_sum = (multiple_entries
140
  .groupby('id')['trendingScore']
141
  .sum()
142
  .reset_index()
143
  .rename(columns={'trendingScore': 'total_score'}))
144
 
145
- # ํ•ฉ์‚ฐ๋œ total_score ์ˆœ์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ ํ›„ ์ƒ์œ„ 20
146
  df_sum = df_sum.sort_values(by='total_score', ascending=False).head(20)
147
 
148
  return df_sum
@@ -151,14 +91,16 @@ def get_top20_multiple_ids(daily_ranks_df):
151
  traceback.print_exc()
152
  return pd.DataFrame()
153
 
 
 
 
154
  def create_score_chart(multiple_ids_df):
155
  """
156
- ์œ„์—์„œ ๋งŒ๋“  df_sum (id + total_score) ๋ฅผ ๊ฐ€์ง€๊ณ ,
157
- id๋ณ„ total_score ๋ง‰๋Œ€ ์ฐจํŠธ๋ฅผ ์ƒ์„ฑ. ์ƒ์œ„ 20๊ฐœ๋งŒ ํ‘œ์‹œ.
158
  """
159
  try:
160
  if multiple_ids_df.empty:
161
- # ์ค‘๋ณต๋œ id ์ž์ฒด๊ฐ€ ํ•˜๋‚˜๋„ ์—†๋Š” ๊ฒฝ์šฐ(๋˜๋Š” ์ง‘๊ณ„๊ฒฐ๊ณผ ์—†์Œ)
162
  placeholder_df = pd.DataFrame({"id": ["No multiple entries"], "total_score": [0]})
163
  fig = px.bar(
164
  placeholder_df,
@@ -167,7 +109,7 @@ def create_score_chart(multiple_ids_df):
167
  orientation='h'
168
  )
169
  fig.update_layout(
170
- title="No multiple entries found in Top 100",
171
  xaxis_title="Total Trending Score",
172
  yaxis_title="Space ID",
173
  plot_bgcolor='white',
@@ -177,17 +119,15 @@ def create_score_chart(multiple_ids_df):
177
  )
178
  return fig
179
 
180
- # ์—ฌ๊ธฐ์„œ multiple_ids_df์—๋Š” ์ด๋ฏธ ์ƒ์œ„ 20๊ฐœ๋งŒ ์žˆ๋Š” ์ƒํƒœ
181
- df = multiple_ids_df.copy()
182
-
183
  fig = px.bar(
184
- df,
185
  y='id',
186
  x='total_score',
187
  orientation='h',
188
- title=f"Top 20 IDs with Multiple Entries in Top 100",
189
- height=400,
190
- text=[f"Score: {score:.2f}" for score in df['total_score']]
191
  )
192
 
193
  fig.update_layout(
@@ -197,7 +137,7 @@ def create_score_chart(multiple_ids_df):
197
  paper_bgcolor='white',
198
  showlegend=False,
199
  margin=dict(l=200, r=20, t=40, b=40),
200
- yaxis={'categoryorder': 'total ascending'}
201
  )
202
 
203
  fig.update_traces(
@@ -214,10 +154,71 @@ def create_score_chart(multiple_ids_df):
214
  traceback.print_exc()
215
  return None
216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  def update_display(selection):
218
  """
219
- ์‚ฌ์šฉ์ž๊ฐ€ ์™ผ์ชฝ/HTML ์นด๋“œ์—์„œ ํŠน์ • id๋ฅผ ์„ ํƒํ–ˆ์„ ๋•Œ,
220
- ๊ทธ id์— ๋Œ€ํ•œ ์ผ๊ฐ„ Trend Chart + ์ƒ์„ธ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜
 
221
  """
222
  global daily_ranks_df
223
 
@@ -227,6 +228,7 @@ def update_display(selection):
227
  try:
228
  space_id = selection
229
 
 
230
  latest_data = daily_ranks_df[
231
  daily_ranks_df['id'] == space_id
232
  ].sort_values('date').iloc[-1]
@@ -256,59 +258,54 @@ def update_display(selection):
256
  print(f"Error in update_display: {e}")
257
  return None, gr.HTML(value=f"<div style='color: red;'>Error processing data: {str(e)}</div>")
258
 
259
- ########## ๋ฉ”์ธ ์‹คํ–‰ ##########
260
-
261
- # ๋ฐ์ดํ„ฐ ๋กœ๋“œ
262
  print("Loading initial data...")
263
- daily_ranks_df, top_100_spaces = load_and_process_data()
264
  print("Data loaded successfully!")
265
 
266
- # '์ตœ์‹  ๋‚ ์งœ์˜ Top100' ์ค‘๋ณต ID ์ง‘๊ณ„ -> ์ƒ์œ„ 20
267
- multiple_ids_df = get_top20_multiple_ids(daily_ranks_df)
268
  score_chart = create_score_chart(multiple_ids_df)
269
 
270
- # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
271
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
272
  gr.Markdown("""
273
  # HF Space Ranking Tracker
274
 
275
- Track, analyze, and discover trending AI applications in the Hugging Face ecosystem.
276
- Our service continuously monitors and ranks all Spaces over a 30-day period,
277
- providing detailed analytics and daily ranking changes for the top 100 performers.
278
  """)
279
 
280
  with gr.Tabs():
281
  with gr.Tab("Dashboard"):
282
  with gr.Row(variant="panel"):
283
- # ์™ผ์ชฝ(Trend Plot)
284
  with gr.Column(scale=7):
285
  trend_plot = gr.Plot(
286
  label="Daily Rank Trend",
287
  container=True
288
  )
289
- # ์˜ค๋ฅธ์ชฝ(Score Chart)
290
  with gr.Column(scale=3):
291
- # multiple_ids_df ๊ธฐ๋ฐ˜ ์ฐจํŠธ
292
  score_plot = gr.Plot(
293
  value=score_chart,
294
  label="Multiple-Entry IDs (Top 20)",
295
  container=True
296
  )
297
 
298
- # Space ์ƒ์„ธ ์ •๋ณด
299
  with gr.Row():
300
  info_box = gr.HTML(
301
  value="<div style='text-align: center; padding: 20px; color: #666;'>Select a space to view details</div>"
302
  )
303
 
304
- # ๋ผ๋””์˜ค ๋ฒ„ํŠผ(๊ฐ€๋ ค๋†“๊ณ  HTML ๋ฒ„ํŠผ -> JS -> ๋ผ๋””์˜ค change ์ด๋ฒคํŠธ ํŠธ๋ฆฌ๊ฑฐ)
305
  space_selection = gr.Radio(
306
- choices=[row['id'] for _, row in top_100_spaces.iterrows()],
307
  value=None,
308
  visible=False
309
  )
310
 
311
- # Top100 ๋ฆฌ์ŠคํŠธ๋ฅผ HTML ์นด๋“œ๋กœ ํ‘œ์‹œ (JS ๋กœ ํด๋ฆญ ์ด๋ฒคํŠธ -> ๋ผ๋””์˜ค ์„ ํƒ)
312
  html_content = """
313
  <div style='display: flex; flex-wrap: wrap; gap: 16px; justify-content: center;'>
314
  """ + "".join([
@@ -320,7 +317,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
320
  border-radius: 8px;
321
  padding: 16px;
322
  margin: 8px;
323
- background-color: hsl(210, {max(30, 90 - (row['rank'] / 100 * 60))}%, {min(97, 85 + (row['rank'] / 100 * 10))}%);
324
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
325
  display: inline-block;
326
  width: 250px;
@@ -354,7 +351,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
354
  </div>
355
  </div>
356
  """
357
- for _, row in top_100_spaces.iterrows()
358
  ]) + """
359
  </div>
360
  <script>
@@ -374,42 +371,20 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
374
 
375
  with gr.Tab("About"):
376
  gr.Markdown("""
377
- ### Our Tracking System
378
-
379
- #### What We Track
380
- - Daily ranking changes for all Hugging Face Spaces
381
- - Comprehensive trending scores based on 30-day activity
382
- - Detailed performance metrics for top 100 Spaces
383
- - Historical ranking data with daily granularity
384
-
385
- #### Key Features
386
- - **Real-time Rankings**: Stay updated with daily rank changes
387
- - **Interactive Visualizations**: Track ranking trajectories over time
388
- - **Trend Analysis**: Identify emerging popular AI applications
389
- - **Direct Access**: Quick links to explore trending Spaces
390
- - **Performance Metrics**: Detailed trending scores and statistics
391
-
392
- ### Why Use HF Space Ranking Tracker?
393
- - Discover trending AI demos and applications
394
- - Monitor your Space's performance and popularity
395
- - Identify emerging trends in the AI community
396
- - Make data-driven decisions about your AI projects
397
- - Stay ahead of the curve in AI application development
398
-
399
- Our dashboard provides a comprehensive view of the Hugging Face Spaces ecosystem,
400
- helping developers, researchers, and enthusiasts track and understand
401
- the dynamics of popular AI applications.
402
 
403
- Whether you're monitoring your own Space's performance or discovering new trending applications,
404
- HF Space Ranking Tracker offers the insights you need.
 
 
405
  """)
406
 
407
- # ๋ผ๋””์˜ค ๋ฒ„ํŠผ change ์ด๋ฒคํŠธ -> update_display ํ•จ์ˆ˜
408
  space_selection.change(
409
  fn=update_display,
410
  inputs=[space_selection],
411
- outputs=[trend_plot, info_box],
412
- api_name="update_display"
413
  )
414
 
415
  if __name__ == "__main__":
 
6
  from io import BytesIO
7
  import traceback
8
 
9
+ ######################################
10
+ # 1) ๋ฐ์ดํ„ฐ ๋กœ๋“œ & 30์ผ์น˜ ๋žญํ‚น ์‚ฐ์ถœ
11
+ ######################################
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  def load_and_process_data():
 
 
 
 
 
13
  try:
14
  url = "https://huggingface.co/datasets/cfahlgren1/hub-stats/resolve/main/spaces.parquet"
15
  response = requests.get(url)
 
20
  df['createdAt'] = pd.to_datetime(df['createdAt'])
21
  df = df[df['createdAt'] >= thirty_days_ago].copy()
22
 
23
+ # 30์ผ ๋™์•ˆ์˜ ๋ชจ๋“  ๋‚ ์งœ์— ๋Œ€ํ•ด ์ˆœํšŒ
24
  dates = pd.date_range(start=thirty_days_ago, end=datetime.now(), freq='D')
25
  daily_ranks = []
26
 
 
31
  date_data = date_data.sort_values(['trendingScore', 'id'], ascending=[False, True])
32
  date_data['rank'] = range(1, len(date_data) + 1)
33
  date_data['date'] = date.date()
 
34
  daily_ranks.append(
35
  date_data[['id', 'date', 'rank', 'trendingScore', 'createdAt']]
36
  )
37
 
 
38
  daily_ranks_df = pd.concat(daily_ranks, ignore_index=True)
39
 
40
+ # ์ตœ์‹  ๋‚ ์งœ์˜ (๋žญํ‚น 1000์œ„ ์ดํ•˜)๋งŒ ์ถ”์ถœ โ† ์—ฌ๊ธฐ์„œ ๋ฒ”์œ„๋ฅผ 1000์œผ๋กœ ํ™•์žฅ!
41
  latest_date = daily_ranks_df['date'].max()
42
+ top_1000_spaces = daily_ranks_df[
43
  (daily_ranks_df['date'] == latest_date) &
44
+ (daily_ranks_df['rank'] <= 1000)
45
  ].sort_values('rank').copy()
46
 
47
+ return daily_ranks_df, top_1000_spaces
48
  except Exception as e:
49
  print(f"Error loading data: {e}")
50
  traceback.print_exc()
51
  return pd.DataFrame(), pd.DataFrame()
52
 
53
+ ######################################
54
+ # 2) ์ค‘๋ณต ID(2๊ฐœ ์ด์ƒ) ํ•ฉ์‚ฐ -> ์ƒ์œ„ 20
55
+ ######################################
56
+ def get_top20_multiple_ids(top_n_spaces_df):
57
  """
58
+ ์ฃผ์–ด์ง„ ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„(์˜ˆ: top_1000_spaces)์—์„œ,
59
+ ๋™์ผํ•œ id๊ฐ€ 2๋ฒˆ ์ด์ƒ ๋“ฑ์žฅํ•˜๋Š” ๊ฒฝ์šฐ 'trendingScore'๋ฅผ ํ•ฉ์‚ฐํ•˜๊ณ ,
60
+ ํ•ฉ์‚ฐ ์ ์ˆ˜๊ฐ€ ๋†’์€ ์ˆœ์œผ๋กœ ์ƒ์œ„ 20๊ฐœ๋งŒ ๋ฐ˜ํ™˜
61
  """
62
+ if top_n_spaces_df.empty:
63
  return pd.DataFrame()
64
 
65
  try:
66
+ # id๋ณ„ ๋“ฑ์žฅ ํšŸ์ˆ˜
67
+ id_counts = top_n_spaces_df['id'].value_counts()
 
 
 
 
 
 
 
 
68
  # 2๊ฐœ ์ด์ƒ ๋“ฑ์žฅํ•˜๋Š” id๋งŒ ์ถ”์ถœ
69
  multiple_ids = id_counts[id_counts >= 2].index
70
 
71
  if len(multiple_ids) == 0:
72
+ # ์ค‘๋ณต id๊ฐ€ ์•„์˜ˆ ์—†์œผ๋ฉด ๋นˆ DF
73
  return pd.DataFrame()
74
 
75
+ # ์ค‘๋ณต๋œ id์— ํ•ด๋‹นํ•˜๋Š” ํ–‰๋งŒ ํ•„ํ„ฐ๋ง
76
+ multiple_entries = top_n_spaces_df[top_n_spaces_df['id'].isin(multiple_ids)].copy()
77
 
78
+ # id๋ณ„ ์Šค์ฝ”์–ด ํ•ฉ์‚ฐ
79
  df_sum = (multiple_entries
80
  .groupby('id')['trendingScore']
81
  .sum()
82
  .reset_index()
83
  .rename(columns={'trendingScore': 'total_score'}))
84
 
85
+ # ํ•ฉ์‚ฐ๋œ total_score ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ -> ์ƒ์œ„ 20
86
  df_sum = df_sum.sort_values(by='total_score', ascending=False).head(20)
87
 
88
  return df_sum
 
91
  traceback.print_exc()
92
  return pd.DataFrame()
93
 
94
+ ######################################
95
+ # 3) ๋ง‰๋Œ€ ์ฐจํŠธ ์ƒ์„ฑ (์ƒ์œ„ 20๊ฐœ)
96
+ ######################################
97
  def create_score_chart(multiple_ids_df):
98
  """
99
+ multiple_ids_df = [ id, total_score ] ํ˜•ํƒœ
 
100
  """
101
  try:
102
  if multiple_ids_df.empty:
103
+ # ์ค‘๋ณต๋œ id๊ฐ€ ์ „ํ˜€ ์—†๋Š” ๊ฒฝ์šฐ (or ๋ฌด์—‡์ธ๊ฐ€ ์ž˜๋ชป๋œ ๊ฒฝ์šฐ)
104
  placeholder_df = pd.DataFrame({"id": ["No multiple entries"], "total_score": [0]})
105
  fig = px.bar(
106
  placeholder_df,
 
109
  orientation='h'
110
  )
111
  fig.update_layout(
112
+ title="No multiple entries found (in Top 1000)",
113
  xaxis_title="Total Trending Score",
114
  yaxis_title="Space ID",
115
  plot_bgcolor='white',
 
119
  )
120
  return fig
121
 
122
+ # ๋ง‰๋Œ€ ์ฐจํŠธ ์ƒ์„ฑ
 
 
123
  fig = px.bar(
124
+ multiple_ids_df,
125
  y='id',
126
  x='total_score',
127
  orientation='h',
128
+ title="Top 20 IDs with Multiple Entries (Rank โ‰ค 1000)",
129
+ text=[f"{score:.2f}" for score in multiple_ids_df['total_score']],
130
+ height=500
131
  )
132
 
133
  fig.update_layout(
 
137
  paper_bgcolor='white',
138
  showlegend=False,
139
  margin=dict(l=200, r=20, t=40, b=40),
140
+ yaxis={'categoryorder': 'total ascending'} # ํฐ ์ ์ˆ˜ ์ˆœ์œผ๋กœ
141
  )
142
 
143
  fig.update_traces(
 
154
  traceback.print_exc()
155
  return None
156
 
157
+ ######################################
158
+ # 4) ์ŠคํŽ˜์ด์Šค ์ƒ์„ธ/ํŠธ๋ Œ๋“œ ์ฐจํŠธ
159
+ ######################################
160
+ def create_trend_chart(space_id, daily_ranks_df):
161
+ """
162
+ ์„ ํƒํ•œ id์— ๋Œ€ํ•œ (30์ผ๊ฐ„) rank ๋ณ€ํ™”๋ฅผ ๋ผ์ธ์ฐจํŠธ๋กœ ํ‘œ์‹œ
163
+ """
164
+ try:
165
+ if space_id is None or daily_ranks_df.empty:
166
+ return None
167
+
168
+ # ํ•ด๋‹น id ํ•„ํ„ฐ๋ง
169
+ space_data = daily_ranks_df[daily_ranks_df['id'] == space_id].copy()
170
+ if space_data.empty:
171
+ return None
172
+
173
+ space_data = space_data.sort_values('date')
174
+
175
+ fig = px.line(
176
+ space_data,
177
+ x='date',
178
+ y='rank',
179
+ title=f'Daily Rank Trend for {space_id}',
180
+ labels={'date': 'Date', 'rank': 'Rank'},
181
+ markers=True,
182
+ height=400
183
+ )
184
+
185
+ # y์ถ•์„ ๋žญํ‚น์ด 1์ด ๊ฐ€์žฅ ๋†’์€ ์ˆœ์ด๋ฏ€๋กœ ๋’ค์ง‘์–ด์„œ ํ‘œ์‹œํ•˜๋ ค๋ฉด range=[100, 1] ๋“ฑ์œผ๋กœ ์„ค์ •
186
+ fig.update_layout(
187
+ xaxis_title="Date",
188
+ yaxis_title="Rank",
189
+ yaxis=dict(
190
+ range=[space_data['rank'].max()+1, 1],
191
+ tickmode='linear',
192
+ tick0=1,
193
+ dtick=10
194
+ ),
195
+ hovermode='x unified',
196
+ plot_bgcolor='white',
197
+ paper_bgcolor='white',
198
+ showlegend=False,
199
+ margin=dict(t=50, r=20, b=40, l=40)
200
+ )
201
+
202
+ fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='lightgray')
203
+ fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='lightgray')
204
+
205
+ fig.update_traces(
206
+ line_color='#2563eb',
207
+ line_width=2,
208
+ marker=dict(size=8, color='#2563eb')
209
+ )
210
+
211
+ return fig
212
+ except Exception as e:
213
+ print(f"Error creating trend chart: {e}")
214
+ traceback.print_exc()
215
+ return None
216
+
217
  def update_display(selection):
218
  """
219
+ ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • id๋ฅผ ์„ ํƒํ–ˆ์„ ๋•Œ,
220
+ 1) ๊ทธ id์˜ ์ผ๊ฐ„ Rank ๋ณ€ํ™”๋ฅผ Trend Chart๋กœ ํ‘œ์‹œ
221
+ 2) ์ƒ์„ธ ์ •๋ณด HTML
222
  """
223
  global daily_ranks_df
224
 
 
228
  try:
229
  space_id = selection
230
 
231
+ # ์ตœ์‹  ๋ฐ์ดํ„ฐ (๊ฐ€์žฅ ๋งˆ์ง€๋ง‰ ๋‚ ์งœ์˜, ํ•ด๋‹น id) ํ•˜๋‚˜ ๊ฐ€์ ธ์˜ค๊ธฐ
232
  latest_data = daily_ranks_df[
233
  daily_ranks_df['id'] == space_id
234
  ].sort_values('date').iloc[-1]
 
258
  print(f"Error in update_display: {e}")
259
  return None, gr.HTML(value=f"<div style='color: red;'>Error processing data: {str(e)}</div>")
260
 
261
+ ######################################
262
+ # ๋ฉ”์ธ
263
+ ######################################
264
  print("Loading initial data...")
265
+ daily_ranks_df, top_n_spaces = load_and_process_data() # ์—ฌ๊ธฐ์„œ n=1000
266
  print("Data loaded successfully!")
267
 
268
+ # ์ค‘๋ณต๋œ ID๊ฐ€ 2๋ฒˆ ์ด์ƒ ๋“ฑ์žฅํ•˜๋Š” ๊ฒƒ๋งŒ ์ง‘๊ณ„ -> ์ƒ์œ„ 20
269
+ multiple_ids_df = get_top20_multiple_ids(top_n_spaces)
270
  score_chart = create_score_chart(multiple_ids_df)
271
 
272
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค
273
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
274
  gr.Markdown("""
275
  # HF Space Ranking Tracker
276
 
277
+ **Note**: ์ด ๋ฐ๋ชจ๋Š” ์‹ค์ œ 'Top 1000'์„ ๋Œ€์ƒ์œผ๋กœ ์ค‘๋ณต๋œ ID(2๊ฐœ ์ด์ƒ)๋ฅผ ์ฐพ์•„ ํ•ฉ์‚ฐ ์Šค์ฝ”์–ด๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
278
+ ๋งŒ์•ฝ ๋ฐ์ดํ„ฐ์— ์ค‘๋ณต ID๊ฐ€ ์—†๋‹ค๋ฉด(๋˜๋Š” ๊ทนํžˆ ์ ๋‹ค๋ฉด), ์šฐ์ธก ๋ง‰๋Œ€ ์ฐจํŠธ๊ฐ€ 'No multiple entries found'์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
 
279
  """)
280
 
281
  with gr.Tabs():
282
  with gr.Tab("Dashboard"):
283
  with gr.Row(variant="panel"):
 
284
  with gr.Column(scale=7):
285
  trend_plot = gr.Plot(
286
  label="Daily Rank Trend",
287
  container=True
288
  )
 
289
  with gr.Column(scale=3):
 
290
  score_plot = gr.Plot(
291
  value=score_chart,
292
  label="Multiple-Entry IDs (Top 20)",
293
  container=True
294
  )
295
 
 
296
  with gr.Row():
297
  info_box = gr.HTML(
298
  value="<div style='text-align: center; padding: 20px; color: #666;'>Select a space to view details</div>"
299
  )
300
 
301
+ # ๋ผ๋””์˜ค ๋ฒ„ํŠผ (Top n=1000)
302
  space_selection = gr.Radio(
303
+ choices=[row['id'] for _, row in top_n_spaces.iterrows()],
304
  value=None,
305
  visible=False
306
  )
307
 
308
+ # HTML ์นด๋“œ (๋žญํ‚น ์ˆœ์œผ๋กœ ํ‘œ์‹œ)
309
  html_content = """
310
  <div style='display: flex; flex-wrap: wrap; gap: 16px; justify-content: center;'>
311
  """ + "".join([
 
317
  border-radius: 8px;
318
  padding: 16px;
319
  margin: 8px;
320
+ background-color: hsl(210, {max(30, 90 - (row['rank'] / 1000 * 60))}%, {min(97, 85 + (row['rank'] / 1000 * 10))}%);
321
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
322
  display: inline-block;
323
  width: 250px;
 
351
  </div>
352
  </div>
353
  """
354
+ for _, row in top_n_spaces.iterrows()
355
  ]) + """
356
  </div>
357
  <script>
 
371
 
372
  with gr.Tab("About"):
373
  gr.Markdown("""
374
+ ### Why might the chart be empty?
375
+ - ์ด ๋ฐ๋ชจ๋Š” Top 1000 ์•ˆ์—์„œ **๋™์ผํ•œ `id`๊ฐ€ 2๋ฒˆ ์ด์ƒ** ๋“ฑ์žฅํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ์ ์ˆ˜๋ฅผ ํ•ฉ์‚ฐํ•ด ๋ง‰๋Œ€์ฐจํŠธ๋ฅผ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
376
+ - ๋ฐ์ดํ„ฐ์…‹ ์ƒ ์‹ค์ œ๋กœ ์ค‘๋ณต๋œ `id`๊ฐ€ ๋งŽ์ง€ ์•Š๋‹ค๋ฉด ์ฐจํŠธ๊ฐ€ ๋น„์–ด์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
 
378
+ ### What can you do?
379
+ - (A) ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด Top 100 โ†’ 1000, 5000 ๋“ฑ์œผ๋กœ ๋Š˜๋ ค๋ณด๊ฑฐ๋‚˜,
380
+ - (B) ์•„์˜ˆ rank ์ œํ•œ ์—†์ด ์ „์ฒด ๋ฐ์ดํ„ฐ์—์„œ ์ค‘๋ณต ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
381
+ - (C) ํ…Œ์ŠคํŠธ์šฉ์œผ๋กœ ๊ฐ€์งœ ์ค‘๋ณต ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด๋„ ๋ฉ๋‹ˆ๋‹ค.
382
  """)
383
 
 
384
  space_selection.change(
385
  fn=update_display,
386
  inputs=[space_selection],
387
+ outputs=[trend_plot, info_box]
 
388
  )
389
 
390
  if __name__ == "__main__":