Related
Relatively new to Dash, and this is a problem that has been vexing me for months now. I am making a multi-page app that shows some basic data trends using cards, and graphs embedded within cardbody. 30% of the time, the app works well without any errors and the other 70% it throws either one of the following:
ImportError: cannot import name 'ValidatorCache' from partially initialized module 'plotly.validator_cache' (most likely due to a circular import)
OR
ImportError: cannot import name 'Layout' from partially initialized module 'plotly.graph_objects' (most likely due to a circular import)
Both these appear quite randomly and I usually refresh the app to make them go away. But obviously I am doing something wrong. I have a set of dropdowns that trigger callbacks on graphs. I have been wracking my head about this. Any help/leads would be appreciated. The only pattern I see in the errors is they seem to emerge when the plotly express graphs are being called in the callbacks.
What am I doing wrong? I have searched all over online for help but nothing yet.
Sharing with some relevant snippets of code (this may be too long and many parts not important to the question, but to give you a general idea of what I have been working towards)
import dash
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
from plotly.subplots import make_subplots
import plotly.express as px
card_content1_1 = [
dbc.CardHeader([dbc.Row([html.H5("SALES VOLUME TREND", className = "font-weight-bold text-success"),
dbc.Button(
html.I(className="fa fa-window-maximize"),
color="success",
id="sales_maximize",
className="ml-auto",
# href="www.cogitaas.com"
)
])]),
dbc.CardBody(
[dcc.Graph(
id='sales_graph',
figure={},
style={'height':'30vh'}
# className="mt-5"
)])]
card_stacked_discount = [
dbc.CardHeader([dbc.Row([html.H5("VOLUMES UNDER DIFFERENT DISCOUNT LEVELS", className="font-weight-bold text-info text-center"),
dbc.Button(
html.I(className="fa fa-window-maximize"),
color="info",
id="discount_maximize",
className="ml-auto",
# href="www.cogitaas.com"
)
])]),
dbc.CardBody(
[dcc.Dropdown(
id = 'stacked_discount_dropdown',
options =stacked_discount_options,
value=stacked_discount_options[0].get('value'),
style={'color':'black'},
# multi=True
),
dcc.Graph(
id='stacked_discount_graph',
figure={},
style={'height':'30vh'}
)])]
cards = html.Div(
[
dbc.Row(
[
dbc.Col(dbc.Card(card_content1_1, color="success", outline=True,
style={'height':'auto'}), width=8),
],
className="mb-4",
),
dbc.Row(
[
dbc.Col(dbc.Card(card_stacked_discount, color="info", outline=True), width=8),
dbc.Col(dbc.Card([
dbc.Row([
dbc.Col(dbc.Card(disc_sub_title, color="info", inverse=True)),
]),
html.Br(),
dbc.Row([
dbc.Col(dbc.Card(disc_sub_card1, color="info", outline=True)),
]),
]), width=4)
],
className="mb-4",
),
]
)
tab1_content = dbc.Card(
dbc.CardBody(
[cards,]
),
className="mt-3",
)
tabs = dbc.Tabs(dbc.Tab(tab1_content, label="Data", label_style={'color':'blue'}, tab_style={"margin-left":"auto"}),])
content = html.Div([
html.Div([tabs]),
],id="page-content")
app.layout = html.Div([dcc.Location(id="url"), content])
#app.callback(
dash.dependencies.Output('sales_graph', 'figure'),
[dash.dependencies.Input('platform-dropdown', 'value'),
dash.dependencies.Input('signature-dropdown', 'value'),
dash.dependencies.Input('franchise-dropdown', 'value'),
dash.dependencies.Input('sales_maximize', 'n_clicks'),
dash.dependencies.Input('time-dropdown', 'value'),
])
def update_sales_graph(plat, sign, fran, maximize, time_per):
print(str(time_per)+"Test")
time_ax=[]
if isinstance(time_per,str):
time_ax.append(time_per)
time_per=time_ax
if (time_per==None) or ('Full Period' in (time_per)):
dff = df[(df.Platform==plat) & (df.Signature==sign) & (df.Franchise==fran)]
elif ('YTD' in time_per):
dff = df[(df.Platform == plat) & (df.Signature == sign) & (df.Franchise == fran) & (df.year==2020)]
else:
dff = df[(df.Platform==plat) & (df.Signature==sign) & (df.Franchise==fran) & (df.Qtr_yr.isin(time_per))]
fig = px.area(dff, x='Date', y='Qty_Orig', color_discrete_sequence=px.colors.qualitative.Dark2)
fig.add_trace(go.Scatter(x=dff['Date'], y=dff['Outliers'], mode = 'markers', name='Outliers',
line=dict(color='darkblue')))
fig.add_trace(go.Scatter(x=dff['Date'], y=dff['bestfit'], name='Long Term Trend',
line=dict(color='darkblue')))
fig.update_layout(font_family="Rockwell",
title={'text': fran + " Volume Trend",
'y': 0.99,
# 'x': 0.15,
# 'xanchor': 'auto',
'yanchor': 'top'
},
legend=dict(
orientation="h",
# y=-.15, yanchor="bottom", x=0.5, xanchor="center"
),
yaxis_visible=False, yaxis_showticklabels=False,
xaxis_title=None,
margin=dict(l=0, r=0, t=0, b=0, pad=0),
plot_bgcolor='White',
paper_bgcolor='White',
)
fig.update_xaxes(showgrid=False, zeroline=True)
fig.update_yaxes(showgrid=False, zeroline=True)
changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0]
if 'maximize' in changed_id:
fig.show()
return fig
#app.callback(
dash.dependencies.Output('stacked_discount_graph', 'figure'),
[dash.dependencies.Input('platform-dropdown', 'value'),
dash.dependencies.Input('signature-dropdown', 'value'),
dash.dependencies.Input('franchise-dropdown', 'value'),
dash.dependencies.Input('discount_maximize', 'n_clicks'),
dash.dependencies.Input('stacked_discount_dropdown', 'value'),
dash.dependencies.Input('time-dropdown', 'value'),
])
def stacked_discount(plat, sign, fran, maximize, sales_days, time_per):
time_ax=[]
if isinstance(time_per,str):
time_ax.append(time_per)
time_per=time_ax
# else:
# time_per=list(time_per)
if (time_per==None) or ('Full Period' in (time_per)):
df_promo = df_promo_vol[(df_promo_vol.Platform==plat) & (df_promo_vol.Signature==sign) & (df_promo_vol.Franchise==fran)]
elif ('YTD' in time_per):
df_promo = df_promo_vol[(df_promo_vol.Platform == plat) & (df_promo_vol.Signature == sign) & (df_promo_vol.Franchise == fran) & (df_promo_vol.Year==2020)]
else:
df_promo = df_promo_vol[(df_promo_vol.Platform==plat) & (df_promo_vol.Signature==sign) & (df_promo_vol.Franchise==fran) & (df_promo_vol.Qtr_yr.isin(time_per))]
color_discrete_map = {
"0 - 10": "orange",
"10 - 15": "green",
"15 - 20": "blue",
"20 - 25": "goldenrod",
"25 - 30": "magenta",
"30 - 35": "red",
"35 - 40": "aqua",
"40 - 45": "violet",
"45 - 50": "brown",
"50 + ": "black"
}
category_orders = {'disc_range': ['0 - 10', '10 - 15', '15 - 20', '20 - 25', '25 - 30', '30 - 35', '35 - 40',
'40 - 45', '45 - 50', '50 + ']}
if (sales_days == None) or (sales_days == 'sales_act'):
fig = px.bar(df_promo, x='start', y='units_shipped', color='disc_range',
color_discrete_map=color_discrete_map,
category_orders=category_orders,
)
else:
fig = px.bar(df_promo, x='start', y='Date', color="disc_range",
color_discrete_map=color_discrete_map,
category_orders=category_orders,
)
fig.update_layout(font_family="Rockwell",
title={'text': fran + " Sales Decomposition",
'y': 0.99,
'x': 0.1,
# 'xanchor': 'auto',
'yanchor': 'top'
},
legend=dict(
orientation="h",
# y=-.15, yanchor="bottom", x=0.5, xanchor="center"
),
# yaxis_visible=False, yaxis_showticklabels=False,
xaxis_title=None,
margin=dict(l=0, r=0, t=30, b=30, pad=0),
plot_bgcolor='White',
paper_bgcolor='White',
)
fig.update_xaxes(showgrid=False, zeroline=True)
fig.update_yaxes(showgrid=False, zeroline=True)
changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0]
if 'maximize' in changed_id:
fig.show()
return fig
Well, it appears I may have stumbled on to an answer. I was using the pretty much the same inputs for multiple callbacks and that could have been causing some interference with the sequencing of inputs. Once I integrated the code into one callback with multiple outputs, the problem seems to have disappeared.
Was dealing with this same issue where everything in my app worked fine, then I made an entirely separate section & callback that started throwing those circular import errors.
Was reluctant to re-arrange my (rightfully) separated callbacks to be just a single one and found you can fix the issue by just simply importing what the script says it's failing to get. In my case, plotly was trying to import the ValidatorCache and Layout so adding these to the top cleared the issue and now my app works as expected. Hope this helps someone experiencing a similar issue.
from plotly.graph_objects import Layout
from plotly.validator_cache import ValidatorCache
Using #ionic-native/ble I'm able to scan and discover a BLE device which has manufacturer specific data.
According to the lib (https://github.com/don/cordova-plugin-ble-central#ios-1) here is the way to get this data
const mfgData = new Uint8Array(device.advertising.kCBAdvDataManufacturerData);
console.log('Manufacturer Data: ', mfgData);
const hex = Buffer.from(mfgData).toString('hex');
console.log(hex);
The encode to hex result being 2604 0504 386 55c0b
What I don't understand is the proper way to use this result to decode the manufacturer (company) id, which is supposed to be "0x0426"
You can try the following :
const toHexString = bytes =>
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
console.log(toHexString(new Uint8Array([0, 1, 2, 42, 100, 101, 102, 255])))
This question already has answers here:
'google-cloud-firestore' distribution doesn't get added to PyInstaller build
(3 answers)
Closed 3 years ago.
I am using a lot of dependencies for my python project. I am using firestore, pyqt5, numpy and some others. I tried creating an executable file using Pyinstaller but whenever I run the file it says "failed to execute script.
Also I am using images folder and 2 other python file as imports in my main.py file
I tried using hidden imports and still wouldn't work. Is there any easy way to create a pyqt5+firestore project?
Main.py
from PyQt5 import QtWidgets
from HomePage import Ui_HomePage
from attendance import Ui_Dialog
class Firstwindow(QtWidgets.QMainWindow, Ui_HomePage):
def __init__(self, parent=None):
super(Firstwindow, self).__init__(parent)
self.setupUi(self)
self.viewAttendance.clicked.connect(self.hide)
class Secondwindow(QtWidgets.QDialog, Ui_Dialog):
def __init__(self, parent=None):
super(Secondwindow, self).__init__(parent)
self.setupUi(self)
self.backButton.clicked.connect(self.hide)
class Manager:
def __init__(self):
self.first = Firstwindow()
self.second = Secondwindow()
self.first.viewAttendance.clicked.connect(self.second.show)
self.second.backButton.clicked.connect(self.first.show)
self.first.show()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
manager = Manager()
sys.exit(app.exec_())
attendance.py
from PyQt5 import QtCore, QtGui, QtWidgets
import firebase_admin
import google.cloud
from firebase_admin import credentials, firestore
cred = credentials.Certificate("******")
app1 = firebase_admin.initialize_app(cred)
"""Creating a database and authorization object"""
DB = firestore.client()
ids = []
names = []
age = []
job = []
phone = []
year = []
date = []
shiftStart = []
shiftEnd = []
store = firestore.client()
peopleRef = store.collection(u'People')
salaryRef = store.collection('Salary')
jobRef = store.collection('Jobs')
recordRef = store.collection('Records')
dates = recordRef.stream()
for dat in dates:
x = store.collection('Records').document(dat.id).collection('Present')
idx = x.stream()
for i in idx:
print(i.id)
print(i.to_dict()['Name'])
jo = jobRef.stream()
for doc in jo:
fo = doc.id
print(fo)
def getData():
try:
docs = peopleRef.stream()
for doc in docs:
ids.append(doc.id)
names.append(doc.to_dict()['Name'])
job.append(doc.to_dict()['Age'])
age.append(doc.to_dict()['Job Type'])
phone.append(doc.to_dict()['Phone'])
except google.cloud.exceptions.NotFound:
print(u'Missing data')
return ids,names,job,age,phone
def clearData():
ids.clear()
names.clear()
age.clear()
job.clear()
phone.clear()
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(595, 337)
Dialog.setWindowFlag(QtCore.Qt.WindowContextHelpButtonHint,False)
self.gridLayout_2 = QtWidgets.QGridLayout(Dialog)
self.gridLayout_2.setObjectName("gridLayout_2")
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout.addItem(spacerItem, 0, 5, 1, 1)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout.addItem(spacerItem1, 0, 3, 1, 1)
self.dateEdit = QtWidgets.QDateEdit(Dialog)
self.dateEdit.setStyleSheet("font: 10pt \"Verdana\";")
self.dateEdit.setObjectName("dateEdit")
self.gridLayout.addWidget(self.dateEdit, 0, 6, 1, 1)
self.refreshButton = QtWidgets.QPushButton(Dialog)
self.refreshButton.setStyleSheet("qproperty-icon:url(:/images/icons/refresh.png);\n"
"background-color: rgb(255, 255, 255);")
self.refreshButton.setText("")
self.refreshButton.setIconSize(QtCore.QSize(20, 20))
self.refreshButton.setFlat(False)
self.refreshButton.setObjectName("refreshButton")
self.gridLayout.addWidget(self.refreshButton, 0, 4, 1, 1)
self.searchDate = QtWidgets.QPushButton(Dialog)
self.searchDate.setStyleSheet("QPushButton {\n"
" color: rgb(0,0,0);\n"
" border: 2px solid #555;\n"
" border-radius: 20px;\n"
" border-style: outset;\n"
" background: rgb(215, 252, 255);\n"
" padding: 5px;\n"
" font: 75 8pt \"Georgia\";\n"
" }\n"
"\n"
"QPushButton:hover {\n"
" background: rgb(111, 210, 255)\n"
" }\n"
"\n"
"QPushButton:pressed {\n"
" border-style: inset;\n"
" background: rgb(111, 210, 255)\n"
" }\n"
"")
self.searchDate.setObjectName("searchDate")
self.gridLayout.addWidget(self.searchDate, 0, 7, 1, 1)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.backButton = QtWidgets.QPushButton(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.backButton.sizePolicy().hasHeightForWidth())
self.backButton.setSizePolicy(sizePolicy)
self.backButton.setStyleSheet("qproperty-icon:url(:/images/icons/back.jpg);\n"
"background-color: rgb(255, 255, 255);")
self.backButton.setText("")
self.backButton.setObjectName("backButton")
self.horizontalLayout.addWidget(self.backButton)
self.nameEntry = QtWidgets.QLineEdit(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.nameEntry.sizePolicy().hasHeightForWidth())
self.nameEntry.setSizePolicy(sizePolicy)
self.nameEntry.setStyleSheet("font: 10pt \"Verdana\";")
self.nameEntry.setObjectName("nameEntry")
self.horizontalLayout.addWidget(self.nameEntry)
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
self.searchName = QtWidgets.QPushButton(Dialog)
self.searchName.setStyleSheet("QPushButton {\n"
" color: rgb(0,0,0);\n"
" border: 2px solid #555;\n"
" border-radius: 20px;\n"
" border-style: outset;\n"
" background: rgb(215, 252, 255);\n"
" padding: 5px;\n"
" font: 75 8pt \"Georgia\";\n"
" }\n"
"\n"
"QPushButton:hover {\n"
" background: rgb(111, 210, 255)\n"
" }\n"
"\n"
"QPushButton:pressed {\n"
" border-style: inset;\n"
" background: rgb(111, 210, 255)\n"
" }\n"
"")
self.searchName.setObjectName("searchName")
self.gridLayout.addWidget(self.searchName, 0, 1, 1, 1)
self.profileTree = QtWidgets.QTableWidget(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.profileTree.sizePolicy().hasHeightForWidth())
self.profileTree.setSizePolicy(sizePolicy)
self.profileTree.setObjectName("profileTree")
self.gridLayout.addWidget(self.profileTree, 1, 0, 1, 1)
self.dataTree = QtWidgets.QTableWidget(Dialog)
self.dataTree.setObjectName("dataTree")
self.gridLayout.addWidget(self.dataTree, 1, 1, 1, 7)
self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1)
self.dataTree.setColumnCount(0)
self.dataTree.setRowCount(0)
self.profileTree.setRowCount(0)
self.profileTree.setColumnCount(0)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
self.profileTree.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers)
self.refreshButton.clicked.connect(self.refresh)
self.profileTree.cellDoubleClicked.connect(self.showClicked)
def showClicked(self,row,col):
idx = self.profileTree.item(row,0).text()
print(idx)
def refresh(self):
profileData = getData()
self.profileTree.setRowCount(len(profileData[0]))
self.profileTree.setColumnCount(2)
self.profileTree.setHorizontalHeaderLabels(('ID', 'Name'))
for row in range(len(profileData[0])):
for col in range(2):
self.profileTree.setItem(row,col,QtWidgets.QTableWidgetItem(profileData[col][row]))
clearData()
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.searchDate.setText(_translate("Dialog", "Search"))
self.searchName.setText(_translate("Dialog", "Search"))
import images
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
Ok so after hours of research I found this post - 'google-cloud-firestore' distribution doesn't get added to PyInstaller build
The problem was with firestore and not PyQt.
I have the following little piece of code (more less as described HERE) - I want to control the number of points to be shown by a slider in shiny. You can see that the initial map is loaded after a little while (watch console output), but it will only show up after you used the slider once.
But I'd like the map to show up after it is created during launch of the shiny app - any hints how to do that?
## app.R ##
library(shiny)
library(shinydashboard)
library(httr)
library(leafletR)
data(quakes)
# dest_dir=tempdir()
dest_dir="foo_map"
dest_file = paste(dest_dir,"quakes","quakes.html",sep="\\")
dat = quakes
createMapHTML <- function(inputFreq=1) {
q.dat <- toGeoJSON(data=dat[seq(from = 1, to = nrow(dat), by=inputFreq), ],
dest=dest_dir, name="quakes")
sty <- styleSingle(col="darkblue", fill="darkblue", rad=6)
# create map
q.map <- leaflet(data=q.dat, dest=dest_dir, size = c(1200, 600), incl.data=TRUE,
base.map=list("osm"), style=sty, popup="*", controls="all")
}
# createMapHTML()
runApp(list(
ui = dashboardPage(
dashboardHeader(title = "quakes"),
dashboardSidebar(
sliderInput("slider", "#observations frequency:", 1, 100, 1)
),
dashboardBody(
htmlOutput("inc")
)
),
server = function(input, output, session) {
createMap <- reactive({
createMapHTML(input$slider)
return(includeHTML(dest_file))
})
output$inc<-renderUI({ createMap() })
}
))
so the bottleneck with the leafletR package is the conversion to GeoJson. Additionally the "includeHTML & htmlOutput" workaround for embedding the html out is flaky..
To avoid both I just switched to the leaflet packackage:
## app.R ##
library(shiny)
library(shinydashboard)
library(leaflet)
data(quakes)
dat = quakes
runApp(list(
ui = dashboardPage(
dashboardHeader(title = "quakes"),
dashboardSidebar(
sliderInput("slider", "#observations frequency:", 1, 100, 1)
),
dashboardBody(
leafletOutput("map", height = 600)
)
),
server = function(input, output) {
output$map <- renderLeaflet({
map <- leaflet() %>% addTiles()
map %>% addCircles(data=dat[seq(from = 1, to = nrow(dat), by=input$slider), ], #input$slider
lat = ~lat, lng = ~long, fillOpacity = 1.0)
})
}
))
I am new to D3.js. I love it but I am having real trouble figuring out the best approach to structuring data.
I would ideally like to create a simple multiline graph that has over points over the selected points. Firstly I have the multiple lines created but trying to add the points has stumped me, and I think it has to do with the structure of my data.
Here is my working fiddle. I'm not sure if I should be trying to use d3.nest to re-arrange the data
I have a json object that I am retrieving from a google form which is all nice and smooth. This is what it looks like:
var data = [{
"description": "Global warming is a serious and pressing problem. We should begin taking steps now even if this involves significant costs",
"year2013": 40,
"year2012": 36,
"year2011": 41,
"year2010": 46,
"year2009": 48,
"year2008": 60,
"year2006": 68,
}, {
"description": "The problem of global warming should be addressed, but its effects will be gradual, so we can deal with the problem gradually by taking steps that are low in cost",
"year2013": 44,
"year2012": 45,
"year2011": 40,
"year2010": 40,
"year2009": 39,
"year2008": 32,
"year2006": 24,
}, {
"description": "Until we are sure that global warming is really a problem, we should not take any steps that would have economic costs",
"year2013": 16,
"year2012": 18,
"year2011": 19,
"year2010": 13,
"year2009": 13,
"year2008": 8,
"year2006": 7,
}, {
"description": "Don't know / refused",
"year2013": 1,
"year2012": 1,
"year2011": 1,
"year2010": 1,
"year2009": 1,
"year2008": 0,
"year2006": 1,
}]
Any help would be appreciated, I have been at it for days.
Cheers!
First - I would flatten your data
data = [
{date:"2011",type: "line0", amount:20}
...
]
Then nest your data by type
nested = d3.nest()
.key( (d) -> return d.type )
.entries(data)
Then append your line groups
# Line Groups
groups = container.selectAll('g.full-line')
.data(nested, (d) -> return d.key )
# ENTER
groups.enter().append('svg:g')
.attr( 'class', (d,i) -> "full-line#{i}" )
# EXIT
d3.transition(groups.exit()).remove()
# TRANSITION
d3.transition(groups)
Then append your chart lines
# Individual Lines
lines = groups.selectAll('.line').data (d)-> [d.values]
# ENTER
lines.enter().append("svg:path")
.attr("class","line")
.attr("d", d3.svg.line()
.interpolate(interpolate)
.defined(defined)
.x( (d,i) -> return xScale(d,i) )
.y( (d,i) -> return yScale(d,i) ) )
# EXIT
d3.transition( groups.exit().selectAll('.line') )
.attr("d",
d3.svg.line()
.interpolate(interpolate)
.defined(defined)
.x( (d,i) -> return xScale(d,i) )
.y( (d,i) -> return yScale(d,i) ) )
# TRANSITION
d3.transition(lines)
.attr("d",
d3.svg.line()
.interpolate(interpolate)
.defined(defined)
.x( (d,i) -> return xScale(d,i) )
.y( (d,i) -> return yScale(d,i) ) )
Thanks
I ended up using something similar.
/* Transform Data */
data = data.map(function (d) {
return {
country: d.country,
date: new Date(d.year.toString()),
value: d.value
};
});
/* Nest Data */
data = d3.nest().key(function (d) {
return d.country;
}).entries(data);`